diff --git a/Documentation/devicetree/bindings/fingerprint_detcet.txt b/Documentation/devicetree/bindings/fingerprint_detcet.txt new file mode 100644 index 000000000000..477195885e2d --- /dev/null +++ b/Documentation/devicetree/bindings/fingerprint_detcet.txt @@ -0,0 +1,4 @@ +fingerprint + +Required Properties: +- compatible: Must be "ompatible = "oneplus,fpdetect". diff --git a/Documentation/devicetree/bindings/input/fpc1020_tee.txt b/Documentation/devicetree/bindings/input/fpc1020_tee.txt new file mode 100644 index 000000000000..c46cb6fc21e0 --- /dev/null +++ b/Documentation/devicetree/bindings/input/fpc1020_tee.txt @@ -0,0 +1,20 @@ +fpc Fingerprint + +Required Properties: +- compatible: Must be "ompatible = "fpc,fpc1020". + + +Example: + + /* Fingerprint */ + fpc_fpc1020 { + compatible = "fpc,fpc1020"; + interrupt-parent = <&tlmm>; + fpc,irq-gpio = <&tlmm 121 0>; + /* + fpc,gpio_id0 = <&tlmm 39 0>; + fpc,gpio_id1 = <&tlmm 41 0>; + fpc,gpio_id2 = <&tlmm 63 0>; + */ + }; + diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_driver_s3320.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_driver_s3320.txt new file mode 100755 index 000000000000..c86c5ed97a11 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_driver_s3320.txt @@ -0,0 +1,9 @@ +synaptics,s3320 + +Required properties: +- compatible : should be "synaptics,s3320" + +Example: + synaptics-rmi-ts@20 { + compatible = "synaptics,s3320"; + }; diff --git a/Documentation/devicetree/bindings/input/tri_state_key.txt b/Documentation/devicetree/bindings/input/tri_state_key.txt new file mode 100644 index 000000000000..60e39093f835 --- /dev/null +++ b/Documentation/devicetree/bindings/input/tri_state_key.txt @@ -0,0 +1,21 @@ +Tri-state key + +Required Properties: +- compatible: Must be "ompatible = "oneplus,tri-state-key". + + +Example: + + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 40 0x00>; + tristate,gpio_key2 = <&tlmm 42 0x00>; + tristate,gpio_key3 = <&tlmm 26 0x00>; + pinctrl-names = "pmx_tri_state_key_active", + "pmx_tri_state_key_suspend"; + pinctrl-0 = <&tri_state_key_active>; + pinctrl-1 = <&tri_state_key_suspend>; + }; + diff --git a/Documentation/devicetree/bindings/misc/bootloader_log.txt b/Documentation/devicetree/bindings/misc/bootloader_log.txt new file mode 100644 index 000000000000..0085c3c0c9bd --- /dev/null +++ b/Documentation/devicetree/bindings/misc/bootloader_log.txt @@ -0,0 +1,9 @@ +Bootloader_log + +Required properties: +- compatible : should be "bootloader_log" + +Example: + bootloader_log { + compatible = "bootloader_log"; + }; diff --git a/Documentation/devicetree/bindings/misc/goodix_fingerprint_tee.txt b/Documentation/devicetree/bindings/misc/goodix_fingerprint_tee.txt new file mode 100644 index 000000000000..68db01911505 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/goodix_fingerprint_tee.txt @@ -0,0 +1,18 @@ +Goodix Fingerprint TEE driver + +Required properties: + +-compatible : should be "goodix,fingerprint". +-interrupt-parent : gpio interrupt parent declare +-fp-gpio-irq : irq gpio declare +-fp-gpio-reset : reset gpio declare + +Example: + +goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + fp-gpio-irq = <&tlmm 58 0x00>; + fp-gpio-reset = <&tlmm 59 0x00>; + status = "okay"; +}; diff --git a/Documentation/devicetree/bindings/power/bq27541.txt b/Documentation/devicetree/bindings/power/bq27541.txt new file mode 100644 index 000000000000..556547277a44 --- /dev/null +++ b/Documentation/devicetree/bindings/power/bq27541.txt @@ -0,0 +1,20 @@ +ti,bq27541-battery + + + +Required properties: + +- compatible : should be "ti,bq27541-battery" + + +Example: + + bq27541-battery@55 { + + status = "ok"; + + compatible = "ti,bq27541-battery"; + + reg = <0x55>; + + qcom,modify-soc-smooth; diff --git a/Documentation/devicetree/bindings/power/oneplus_fastchg.txt b/Documentation/devicetree/bindings/power/oneplus_fastchg.txt new file mode 100644 index 000000000000..6ba9145f5eb3 --- /dev/null +++ b/Documentation/devicetree/bindings/power/oneplus_fastchg.txt @@ -0,0 +1,21 @@ +crochip,oneplus_fastcg" + + + +Required properties: + +- compatible : should be "microchip,oneplus_fastcg" + + +Example: + oneplus_fastcg@26{ + + status = "ok"; + + compatible = "microchip,oneplus_fastcg"; + + reg = <0x26>; + + microchip,mcu-en-gpio = <&tlmm 96 0x00>; + + microchip,usb-sw-1-gpio = <&tlmm 15 0x00>; diff --git a/Documentation/devicetree/bindings/serial/msm_serial.txt b/Documentation/devicetree/bindings/serial/msm_serial.txt new file mode 100644 index 000000000000..1938e537500c --- /dev/null +++ b/Documentation/devicetree/bindings/serial/msm_serial.txt @@ -0,0 +1,17 @@ +oem force serial + +Required properties: +- compatible : should be "oem,force_serial" + +Example: + force_enable_serial { + compatible = "oem,force_serial"; + }; + +Required properties: +- compatible : should be "em,oem_serial_pinctrl" + +Example: + oem_serial_pinctrl { + compatible = "oem,oem_serial_pinctrl"; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/op_rf_cable_monitor.txt b/Documentation/devicetree/bindings/soc/qcom/op_rf_cable_monitor.txt new file mode 100644 index 000000000000..3da1370476f6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/op_rf_cable_monitor.txt @@ -0,0 +1,9 @@ +Op_rf_cable_monitor + +Required properties: +- compatible : should be "oem,rf_cable" + +Example: + oem_rf_cable { + compatible = "oem,rf_cable"; + }; \ No newline at end of file diff --git a/Documentation/devicetree/bindings/sound/tfa98xx.txt b/Documentation/devicetree/bindings/sound/tfa98xx.txt new file mode 100644 index 000000000000..22ae56e9e187 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tfa98xx.txt @@ -0,0 +1,9 @@ +nxp,tfa9891 + +Required properties: +- compatible : should be "nxp,tfa9891" + +Example: + bootloader_log { + compatible = "nxp,tfa9891"; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index f9097941c192..552b0ce6cc87 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -262,3 +262,8 @@ zyxel ZyXEL Communications Corp. zarlink Zarlink Semiconductor zii Zodiac Inflight Innovations zte ZTE Corp. +oem Oneplus Technologies +oneplus OnePlus Technologies +fpc fpc1020 +HWK synaptics +android Android P diff --git a/Makefile b/Makefile index 0c5b530ecf02..01799b2db6d1 100644 --- a/Makefile +++ b/Makefile @@ -414,6 +414,8 @@ KBUILD_AFLAGS_MODULE := -DMODULE KBUILD_CFLAGS_MODULE := -DMODULE KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds +CFLAGS_KERNEL += -DJUST_FOR_BRINGUP + # Read KERNELRELEASE from include/config/kernel.release (if it exists) KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile index 0bd48399205b..d77dd0faceb3 100644 --- a/arch/arm/boot/dts/qcom/Makefile +++ b/arch/arm/boot/dts/qcom/Makefile @@ -145,40 +145,48 @@ msm8998-qrd-overlay.dtbo-base := msm8998-qrd.dtb msm8998-qrd-vr1-overlay.dtbo-base := msm8998-qrd-vr1.dtb msm8998-qrd-skuk-overlay.dtbo-base := msm8998-qrd-skuk.dtb else -dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ - msm8998-rumi.dtb \ - msm8998-cdp.dtb \ - msm8998-mtp.dtb \ - msm8998-qrd.dtb \ - msm8998-v2-sim.dtb \ - msm8998-v2-rumi.dtb \ - msm8998-v2-mtp.dtb \ - msm8998-v2-cdp.dtb \ - msm8998-v2-qrd.dtb \ - msm8998-qrd-skuk.dtb \ - msm8998-v2-qrd-skuk.dtb \ - msm8998-qrd-vr1.dtb \ - msm8998-v2-qrd-vr1.dtb \ - msm8998-v2-qrd-skuk-evt3.dtb \ - msm8998-v2-qrd-skuk-hdk.dtb \ - apq8098-mtp.dtb \ - apq8098-cdp.dtb \ - apq8098-v2-mtp.dtb \ - apq8098-v2-cdp.dtb \ - apq8098-v2-qrd.dtb \ - apq8098-v2-qrd-skuk-hdk.dtb \ - msm8998-v2.1-mtp.dtb \ - msm8998-v2.1-mtp-4k-display.dtb \ - msm8998-v2.1-cdp.dtb \ - msm8998-v2.1-qrd.dtb \ - apq8098-v2.1-mtp.dtb \ - apq8098-v2.1-cdp.dtb \ - apq8098-v2.1-qrd.dtb \ - apq8098-v2.1-mediabox.dtb \ - apq8098-v2.1-svr20.dtb \ - msm8998-v2.1-interposer-sdm660-cdp.dtb \ - msm8998-v2.1-interposer-sdm660-mtp.dtb \ - msm8998-v2.1-interposer-sdm660-qrd.dtb +#dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \ +# msm8998-rumi.dtb \ +# msm8998-cdp.dtb \ +# msm8998-mtp.dtb \ +# msm8998-qrd.dtb \ +# msm8998-v2-sim.dtb \ +# msm8998-v2-rumi.dtb \ +# msm8998-v2-mtp.dtb \ +# msm8998-v2-cdp.dtb \ +# msm8998-v2-qrd.dtb \ +# msm8998-qrd-skuk.dtb \ +# msm8998-v2-qrd-skuk.dtb \ +# msm8998-qrd-vr1.dtb \ +# msm8998-v2-qrd-vr1.dtb \ +# msm8998-v2-qrd-skuk-evt3.dtb \ +# msm8998-v2-qrd-skuk-hdk.dtb \ +# apq8098-mtp.dtb \ +# apq8098-cdp.dtb \ +# apq8098-v2-mtp.dtb \ +# apq8098-v2-cdp.dtb \ +# apq8098-v2-qrd.dtb \ +# apq8098-v2-qrd-skuk-hdk.dtb \ +# msm8998-v2.1-mtp.dtb \ +# msm8998-v2.1-mtp-4k-display.dtb \ +# msm8998-v2.1-cdp.dtb \ +# msm8998-v2.1-qrd.dtb \ +# apq8098-v2.1-mtp.dtb \ +# apq8098-v2.1-cdp.dtb \ +# apq8098-v2.1-qrd.dtb \ +# apq8098-v2.1-mediabox.dtb \ +# apq8098-v2.1-svr20.dtb \ +# msm8998-v2.1-interposer-sdm660-cdp.dtb \ +# msm8998-v2.1-interposer-sdm660-mtp.dtb \ +# msm8998-v2.1-interposer-sdm660-qrd.dtb \ + +dtb-$(CONFIG_ARCH_MSM8998) += cheeseburger-v2.1-dvt1.dtb +dtb-$(CONFIG_ARCH_MSM8998) += cheeseburger-v2.1-pvt.dtb +dtb-$(CONFIG_ARCH_MSM8998) += cheeseburger-v2.1-pvt1.dtb +dtb-$(CONFIG_ARCH_MSM8998) += dumpling-v2.1-dvt.dtb +dtb-$(CONFIG_ARCH_MSM8998) += dumpling-v2.1-pvt.dtb +dtb-$(CONFIG_ARCH_MSM8998) += dumpling-v2.1-Second_Resource.dtb + endif dtb-$(CONFIG_ARCH_MSMHAMSTER) += msmhamster-rumi.dtb diff --git a/arch/arm/boot/dts/qcom/OP-batterydata-3300mah.dtsi b/arch/arm/boot/dts/qcom/OP-batterydata-3300mah.dtsi new file mode 100644 index 000000000000..0ecee72799b3 --- /dev/null +++ b/arch/arm/boot/dts/qcom/OP-batterydata-3300mah.dtsi @@ -0,0 +1,80 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +qcom,OP_3300mah { + qcom,max-voltage-uv = <4370000>; + qcom,fg-cc-cv-threshold-mv = <4360>; + qcom,fastchg-current-ma = <3000>; + qcom,batt-id-kohm = <200>; + qcom,battery-beta = <3450>; + qcom,battery-type = "OP_3300mah"; + qcom,checksum = <0xE06B>; + qcom,gui-version = "PMI8998GUI - 0.0.0.82"; + qcom,fg-profile-data = [ + A4 1F 6E 05 + 9C 0A 16 06 + 32 1D 24 E5 + 61 0B 1B 15 + AD 17 8C 22 + EB 3C 87 4A + 5B 00 00 00 + 12 00 00 00 + 00 00 62 C2 + 0C CD D8 C2 + 19 00 0C 00 + 7E 00 C7 EC + E3 05 5D FA + 97 F5 12 12 + C2 05 90 3B + 22 09 40 40 + 07 00 05 00 + 7D 1F DE 05 + 3F 0A 73 06 + 72 1D E2 F5 + 6F 12 BF 1D + 88 18 FB 22 + 8D 45 C6 52 + 54 00 00 00 + 0F 00 00 00 + 00 00 BD CD + 55 C2 5D C5 + 14 00 00 00 + 7E 00 C7 EC + 60 06 BB 00 + B3 FC 61 03 + 6A 06 78 1B + B3 33 08 33 + 07 10 00 00 + 3E 0B 99 45 + 14 00 19 00 + AE 01 0A FA + FF 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + ]; +}; diff --git a/arch/arm/boot/dts/qcom/cheeseburger-dvt1.dtsi b/arch/arm/boot/dts/qcom/cheeseburger-dvt1.dtsi new file mode 100644 index 000000000000..8126bef7892e --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-dvt1.dtsi @@ -0,0 +1,1148 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fa3_1080p_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + +&soc { + tlmm: pinctrl@03400000 { + tri_state_key_mux { + tri_state_key_active: tri_state_key_active { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + + tri_state_key_suspend: tri_state_key_suspend { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; + /* Tri-state key */ + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 40 0x00>; + tristate,gpio_key2 = <&tlmm 42 0x00>; + tristate,gpio_key3 = <&tlmm 26 0x00>; + + pinctrl-names = "pmx_tri_state_key_active", "pmx_tri_state_key_suspend"; + pinctrl-0 = <&tri_state_key_active>; + pinctrl-1 = <&tri_state_key_suspend>; + }; + +}; + +//camera +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398";// eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350";// eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371";// eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; + diff --git a/arch/arm/boot/dts/qcom/cheeseburger-pvt.dtsi b/arch/arm/boot/dts/qcom/cheeseburger-pvt.dtsi new file mode 100644 index 000000000000..02068efcfe74 --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-pvt.dtsi @@ -0,0 +1,1103 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fa3_1080p_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: + cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: + cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: + cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398";// eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350";// eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371";// eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; + diff --git a/arch/arm/boot/dts/qcom/cheeseburger-pvt1.dtsi b/arch/arm/boot/dts/qcom/cheeseburger-pvt1.dtsi new file mode 100644 index 000000000000..ca2be48123f0 --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-pvt1.dtsi @@ -0,0 +1,1104 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62"; + }; + config { + pins = "gpio94", "gpio21", "gpio62"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fa3_1080p_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + + +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: + cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: + cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: + cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* + gpios = <&tlmm 27 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* + gpios = <&tlmm 6 0>; + qcom,gpio-vaf = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398";// eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350";// eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371";// eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; + diff --git a/arch/arm/boot/dts/qcom/cheeseburger-v2.1-dvt1.dts b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-dvt1.dts new file mode 100644 index 000000000000..a32d5b08d8ac --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-dvt1.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "cheeseburger.dtsi" +#include "cheeseburger-dvt1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 16859 22>; +}; diff --git a/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt.dts b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt.dts new file mode 100644 index 000000000000..c07a4ae53261 --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "cheeseburger.dtsi" +#include "cheeseburger-pvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 16859 23>; +}; diff --git a/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt1.dts b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt1.dts new file mode 100644 index 000000000000..4c5370632f1e --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger-v2.1-pvt1.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "cheeseburger.dtsi" +#include "cheeseburger-pvt1.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 16859 24>; +}; diff --git a/arch/arm/boot/dts/qcom/cheeseburger.dtsi b/arch/arm/boot/dts/qcom/cheeseburger.dtsi new file mode 100755 index 000000000000..d4285b6e9022 --- /dev/null +++ b/arch/arm/boot/dts/qcom/cheeseburger.dtsi @@ -0,0 +1,1035 @@ +/* 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 + * 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 "pxlw-iris-settings.dtsi" + +/ { + reserved-memory { + ramoops_mem: ramoops_mem@ac000000 { + reg = <0 0xAC000000 0 0x00200000>; + label = "ramoops_mem"; + }; + param_mem: param_mem@ac200000 { + reg = <0 0xAC200000 0 0x00100000>; + label = "param_mem"; + }; + mtp_mem: mtp_mem@ac400000 { + reg = <0 0xAC400000 0 0x00B00000>; + label = "mtp_mem"; + }; + bootloader_log_mem: bootloader_log_mem@9fff7000 { + reg = <0 0x9FFF7000 0 0x00100000>; + label = "bootloader_log_mem"; + }; + }; +}; + +&pil_ipa_gpu_mem{ + reg = <0 0x95600000 0 0x100000>;//move start address +4M to match modem aligned +}; + +&pil_slpi_mem{ + reg = <0 0x94700000 0 0xf00000>;//move start address +4M to match modem aligned +}; + +&pil_mba_mem{ + reg = <0 0x94500000 0 0x200000>;//move start address +4M to match modem aligned +}; + +&pil_video_mem{ + reg = <0 0x94000000 0 0x500000>;//move start address +4M to match modem aligned +}; + +&modem_mem{ + reg = <0 0x8d000000 0 0x7000000>;//move start address +4M to match modem aligned +}; + +&pil_adsp_mem{ + reg = <0 0x8b200000 0 0x1e00000>; //increae 4M size by oneplus +}; + +/* I/O */ +&soc { + gpio_keys { + vol_up { + /delete-property/ gpio-key,wakeup; + }; + vol_down { + label = "volume_down"; + gpios = <&pm8998_gpios 5 0x1>; + linux,input-type = <1>; + linux,code = <114>; + debounce-interval = <15>; + }; + hallsensor_key { + label = "hallsensor_key"; + gpios = <&tlmm 124 1>; + interrupt-parent = <&tlmm>; + interrupts = <124 0x0>; + linux,input-type = <5>; + linux,code = <0>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; + + tlmm: pinctrl@03400000 { + tri_state_key_mux { + tri_state_key_active: tri_state_key_active { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + + tri_state_key_suspend: tri_state_key_suspend { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + oem_rf_cable_mux { + oem_rf_cable_active: oem_rf_cable_active { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + oem_rf_cable_suspend: oem_rf_cable_suspend { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + + }; + }; + }; + + /* Tri-state key */ + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 40 0x00>; + tristate,gpio_key2 = <&tlmm 42 0x00>; + tristate,gpio_key3 = <&tlmm 26 0x00>; + + pinctrl-names = "pmx_tri_state_key_active", "pmx_tri_state_key_suspend"; + pinctrl-0 = <&tri_state_key_active>; + pinctrl-1 = <&tri_state_key_suspend>; + }; + + oem_rf_cable { + compatible = "oem,rf_cable"; + interrupt-parent = <&tlmm>; + rf,cable-gpio = <&tlmm 36 0>; + pinctrl-names = "oem_rf_cable_active", "oem_rf_cable_suspend"; + pinctrl-0 = <&oem_rf_cable_active >; + pinctrl-1 = <&oem_rf_cable_suspend>; + }; + + qcom,qbt1000 { + status = "disabled"; + }; + + /* Fingerprint */ + fpc_fpc1020 { + compatible = "fpc,fpc1020"; + interrupt-parent = <&tlmm>; + fpc,irq-gpio = <&tlmm 121 0>; + /* + fpc,gpio_id0 = <&tlmm 39 0>; + fpc,gpio_id1 = <&tlmm 41 0>; + fpc,gpio_id2 = <&tlmm 63 0>; + */ + }; + + goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + fp-gpio-irq = <&tlmm 121 0x00>; + fp-gpio-reset = <&tlmm 75 0x00>; + status = "okay"; + }; + + tlmm: pinctrl@03400000 { + fp_enable: fp_enable { + mux { + pins = "gpio25"; + function = "gpio"; + }; + + config { + pins = "gpio25"; + drive-strength = <2>; + bias-pull-up; + output-enable; + output-high; + }; + }; + fp_id0_init: fp_id0_init { + mux { + pins = "gpio39"; + function = "gpio"; + }; + + config { + pins = "gpio39"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + fp_id1_init: fp_id1_init { + mux { + pins = "gpio41"; + function = "gpio"; + }; + + config { + pins = "gpio41"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + fp_id2_init: fp_id2_init { + mux { + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + }; + + fingerprint_detect { + compatible = "oneplus,fpdetect"; + fp-gpio-enable = <&tlmm 25 0>; + fp-gpio-id0 = <&tlmm 39 0>; + fp-gpio-id1 = <&tlmm 41 0>; + fp-gpio-id2 = <&tlmm 63 0>; + pinctrl-names = "fp_enable", "fp_id_init"; + pinctrl-0 = <&fp_enable>; + pinctrl-1 = <&fp_id0_init &fp_id1_init &fp_id2_init>; + }; + + qcom,bcl { + qcom,bcl-soc-hotplug-list = <&CPU6 &CPU7>; + qcom,ibat-monitor { + qcom,soc-low-threshold = <5>; + }; + }; + ramoops { + compatible = "ramoops"; + status = "ok"; + memory-region = <&ramoops_mem>; + }; + + /* Add 0x9c 0x21C, 0xf0 0x23C */ + qusb_phy0: qusb@c012000 { + qcom,qusb-phy-init-seq = + /* */ + <0x80 0x0 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x9c 0x21C + 0xf0 0x23C + 0x0f 0x240>; + }; + force_enable_serial { + compatible = "oem,force_serial"; + }; + oem_serial_pinctrl { + compatible = "oem,oem_serial_pinctrl"; + pinctrl-names = "uart_pinctrl_active", "uart_pinctrl_deactive"; + pinctrl-0 = <&uart_console_active>; + pinctrl-1 = <&uart_console_deactive>; + }; + bootloader_log { + compatible = "bootloader_log"; + linux,contiguous-region = <&bootloader_log_mem>; + }; +}; + + + + + + +&pm8998_gpios { + /* GPIO 5 for Vol- Key */ + gpio@c400 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; +}; + +&pm8998_rtc { + compatible = "qcom,qpnp-rtc"; + qcom,qpnp-rtc-alarm-pwrup = <1>; +}; + +&pmi8998_gpios { + /* button-backlight */ + gpio@c400 { + qcom,mode = <1>; /* Output */ + qcom,pull = <4>; /* Pull Down*/ + qcom,master-en = <1>; /* Enable GPIO */ + qcom,invert = <1>; + qcom,out-strength = <1>; + status = "okay"; + }; +}; + +&sdhc_2 { + status = "disabled"; +}; + +&pmi8998_haptics { + status = "okay"; + qcom,wave-shape = "sine"; + qcom,play-mode = "buffer"; + qcom,wave-samples = [28 28 28 28 28 28 28 28]; + qcom,wave-samples-overdrive = [7e 7e 7e 7e 7e 28 28 28]; + qcom,brake-pattern = [03 03 03 03]; + qcom,wave-rep-cnt = <1>; + qcom,wave-samp-rep-cnt = <1>; + qcom,use-play-irq; + qcom,vmax-mv = <2088>; + qcom,wave-play-rate-us = <4255>; + qcom,lra-auto-res-mode = "zxd-eop"; + qcom,lra-res-cal-period = <32>; +}; + +&spmi_bus { + qcom,pmi8998@2 { + /* button-backlight */ + gpio-leds { + compatible = "gpio-leds"; + status = "okay"; + vdd-supply = <&pmi8998_bob>; + keypad-led-vbob-min = <3312000>; + keypad-led-vbob-max = <3600000>; + keypad-backlight { + gpios = <&pmi8998_gpios 5 0>; //here maybe pm8998_gpios, depends on what you use. + label = "button-backlight"; + linux,default-trigger = "none"; + default-state = "off"; + }; + }; + }; + + qcom,pm8998@0{ + qcom,power-on@800 { + qcom,pon-dbc-delay = <31250>; + qcom,kpdpwr-sw-debounce; + }; + }; + qcom,pmi8998@3 { + /* RGB LED blink */ + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&red_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <1>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + +&green_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <13>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + +&blue_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <13>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + +/* Charging */ +/*&soc { + tlmm: pinctrl@03400000 { + oneplus_fastchg { + usb_sw_active: usb_sw_active { + mux { + pins = "gpio15", "gpio16"; + function = "gpio"; + }; + + config { + pins = "gpio15", "gpio16"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + usb_sw_suspend: usb_sw_suspend { + mux { + pins = "gpio15", "gpio16"; + function = "gpio"; + }; + + config { + pins = "gpio15", "gpio16"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; +};*/ + +/* Charging */ +&soc { + tlmm: pinctrl@03400000 { + oneplus_fastchg { + usb_sw_active: usb_sw_active { + mux { + pins = "gpio90", "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio90", "gpio91"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + usb_sw_suspend: usb_sw_suspend { + mux { + pins = "gpio90", "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio90", "gpio91"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; +}; + +&i2c_7 { + bq27541-battery@55 { + status = "ok"; + compatible = "ti,bq27541-battery"; + reg = <0x55>; + qcom,modify-soc-smooth; + }; + oneplus_fastcg@26{ + status = "ok"; + compatible = "microchip,oneplus_fastcg"; + reg = <0x26>; + microchip,mcu-en-gpio = <&tlmm 96 0x00>; + microchip,usb-sw-1-gpio = <&tlmm 90 0x00>; + microchip,usb-sw-2-gpio = <&tlmm 91 0x00>; + microchip,ap-clk = <&tlmm 79 0x00>; + microchip,ap-data = <&tlmm 80 0x00>; + pinctrl-names = "mux_fastchg_active", + "mux_fastchg_suspend", + "mcu_data_active", + "mcu_data_suspend"; + pinctrl-0 = <&fastchg_active + &usb_sw_active + &ap_clk_active >; + pinctrl-1 = <&usb_sw_suspend + &fastchg_suspend + &ap_clk_suspend>; + pinctrl-2 =<&ap_data_active>; + pinctrl-3 =<&ap_data_suspend>; + clocks = <&clock_gcc clk_uart_snoc_msmbus_a_clk>, + <&clock_gcc clk_uart_cnoc_msmbus_a_clk>; + clock-names = "snoc", "cnoc"; + }; +}; +&soc { + tlmm: pinctrl@03400000 { + oneplus_fastchg { + fastchg_active: fastchg_active { + mux { + pins = "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio96"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + fastchg_suspend: fastchg_suspend { + mux { + pins = "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio96"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_clk_active: ap_clk_active { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_clk_suspend: ap_clk_suspend { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_data_active: ap_data_active { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_data_suspend: ap_data_suspend { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; +}; + +&pmi8998_charger { + /*Set FCC MAX 500mA by default,FCC will*/ + /*be set again after check battery temperature*/ + qcom,fcc-max-ua = <500000>; + qcom,usb-icl-ua = <1800000>; + qcom,fv-max-uv = <4365000>; + /* ibatmax setting for different temp regions */ + ibatmax-little-cold-ma = <325>; + ibatmax-cool-ma = <450>; + ibatmax-little-cool-ma = <775>; + ibatmax-pre-normal-ma = <1425>; + ibatmax-normal-ma = <1950>; + ibatmax-warm-ma = <750>; + /* vbatmax setting for different temp regions */ + vbatmax-little-cold-mv = <3975>; //3980 + vbatmax-cool-mv = <4380>; + vbatmax-little-cool-mv = <4380>; + vbatmax-pre-normal-mv = <4380>; + vbatmax-normal-mv = <4380>; + vbatmax-warm-mv = <4080>; + /* vbatdet setting for different temp regions */ + vbatdet-little-cold-mv = <3700>; + vbatdet-cool-mv = <4150>; + vbatdet-little-cool-mv = <4270>; + vbatdet-pre-normal-mv = <4270>; + vbatdet-normal-mv = <4270>; + vbatdet-warm-mv = <3980>; + /* temp region settings */ + cold-bat-decidegc = <30>; + little-cold-bat-decidegc = <0>; + cool-bat-decidegc = <50>; + little-cool-bat-decidegc = <120>; + pre-normal-bat-decidegc = <160>; + warm-bat-decidegc = <450>; + hot-bat-decidegc = <530>; + /* other settings */ + qcom,cutoff-voltage-with-charger = <3250>; + disable-pd; +}; + +&pmi8998_fg { + oem,use_external_fg; + qcom,fg-rsense-sel = <0>; +}; + + +/* Display */ +&i2c_5 { + synaptics@20 { + status = "disabled"; + }; + synaptics-rmi-ts@20 { + compatible = "synaptics,s3320"; + reg = <0x20>; + interrupt-parent = <&tlmm>; + interrupts = <125 0x2008>; + vcc_i2c_1v8-supply = <&pm8998_l6>; + vdd_2v8-supply = <&pm8998_l28>; + synaptics,tx-rx-num = <17 30>; + synaptics,vdd-voltage = <1808000 1808000>; + synaptics,avdd-voltage = <3008000 3008000>; + synaptics,vdd-current = <40000>; + synaptics,avdd-current = <20000>; + synaptics,display-coords = <1080 1920>; + synaptics,reset-gpio = <&tlmm 89 0x00>; + synaptics,irq-gpio = <&tlmm 125 0x2008>; + oem,support_hw_poweroff; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + }; +}; +&pcie0_clkreq_default { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; +}; + +&mdss_dp_ctrl { + status = "disabled"; +}; + +&mdss_dp_pll { + status = "disabled"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi1 { + status = "disabled"; +}; + +&labibb { + status = "disabled"; +}; + +&mdss_hdmi_tx { + status = "disabled"; +}; + +&mdss_hdmi_pll { + status = "disabled"; +}; + +&snd_9335 { + qcom,mbhc-audio-jack-type = "4-pole-jack"; +}; +/* + * liuhaituo@MultiMediaAudio modified for adapation phone + * headset and AKG headset-mic 2018/4/26 + */ +&slim_aud { + tasha_codec { + qcom,cdc-micbias1-mv = <1800>; + qcom,cdc-micbias2-mv = <2700>; + qcom,cdc-micbias3-mv = <1800>; + qcom,cdc-micbias4-mv = <1800>; + }; +}; + +/* Audio */ +&soc { + i2c@c1b7000 { + status = "ok"; + test_tfa9891: tfa9891@36 { + compatible = "nxp,tfa9891"; + reg = <0x36>; + reset-gpio= <&tlmm 49 0>; + irq-gpio= <&tlmm 50 0>; + bob_power-supply = <&pmi8998_bob>; + qcom,bob_power-voltage-level = <2950000 2960000>; + qcom,bob_power-current-level = <200 800000>; + status = "ok"; + }; + }; + + sound-9335 { + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "AMIC3", "MIC BIAS2", + "MIC BIAS2", "ANCRight Headset Mic", + "AMIC4", "MIC BIAS2", + "MIC BIAS2", "ANCLeft Headset Mic", + "AMIC5", "MIC BIAS3", + "MIC BIAS3", "Handset Mic", + "AMIC6", "MIC BIAS4", + "MIC BIAS4", "Analog Mic6", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS4", + "MIC BIAS4", "Digital Mic2", + "DMIC3", "MIC BIAS4", + "MIC BIAS4", "Digital Mic3", + "DMIC4", "MIC BIAS3", + "MIC BIAS3", "Digital Mic4", + "DMIC5", "MIC BIAS3", + "MIC BIAS3", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + qcom,us-euro-gpios = <>; + qcom,msm-mbhc-hphl-swh = <1>; + qcom,msm-mbhc-gnd-swh = <1>; + qcom,wsa-max-devs = <>; + qcom,wsa-devs = <>; + qcom,wsa-aux-dev-prefix = <>; + }; + + sound-tavil { + qcom,usbc-analog-en1_gpio = <>; + qcom,usbc-analog-en2_n_gpio = <>; + }; +}; + +&i2c_9 { /* BLSP2 QUP9 for Audio Speaker */ + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + status = "ok"; +}; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + qcom,msm-mi2s-slave = <0>; + qcom,msm-mi2s-ext-mclk = <0>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active + &quat_mi2s_sd0_active + &quat_mi2s_sd1_active>; + pinctrl-1 = <&quat_mi2s_sleep + &quat_mi2s_sd0_sleep + &quat_mi2s_sd1_sleep>; +}; + +&wcd_us_euro_gpio { + status = "disabled"; +}; + +&wcd_usbc_analog_en1_gpio { + status = "disabled"; +}; + +&wcd_usbc_analog_en2n_gpio { + status = "disabled"; +}; +&clock_audio_lnbb { + status = "disabled"; +}; + +/* Camera */ +&cci { + qcom,eeprom@1 { + status = "disabled"; + }; + + qcom,camera@1 { + status = "disabled"; + }; +}; + +&firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + }; + }; +}; + +/*Anderson-Config_UARTPIN_as_GPIO*[*/ +&soc { + tlmm: pinctrl@03400000 { + uart_console_deactive: uart_console_deactive { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; +}; + +&uartblsp2dm1 { + pinctrl-names = "uart_active", "uart_deactive"; + pinctrl-0 = <&uart_console_active>; + pinctrl-1 = <&uart_console_deactive>; +}; +/*Anderson-Config_UARTPIN_as_GPIO*]**/ + +/* External Thermistors */ +&pm8998_vadc { + chan@4e { + label = "ufs_therm"; + reg = <0x4e>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@4f { + label = "pa_therm0"; + reg = <0x4f>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@50 { + label = "pa_therm1"; + reg = <0x50>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; +}; + +&pm8998_adc_tm { + chan@4e { + label = "ufs_therm"; + reg = <0x4e>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x80>; + qcom,thermal-node; + }; + + chan@4f { + label = "pa_therm0"; + reg = <0x4f>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x88>; + qcom,thermal-node; + }; + + chan@50 { + label = "pa_therm1"; + reg = <0x50>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x90>; + qcom,thermal-node; + }; +}; + + +/*pcie 0 don't used,gpio 37 will used in nfc.*/ +/*but gpio37 will request in pcie0. so disable it*/ +&pcie0 { + status = "disabled"; +}; + +&pm8998_gpios { + gpio@d400 { + qcom,pull = <4>; + }; +}; + +&spi_1 { + status = "ok"; + qcom,disable-autosuspend; + + ese@0 { + compatible = "nxp,p61"; + reg = <0>; + spi-max-frequency = <8000000>; + }; +}; + +&i2c_6 { /* BLSP1 QUP6 (NFC) */ + nq@28 { + status = "disabled"; + }; + + pn5xx@28 { + compatible = "nxp,pn544"; + reg = <0x28>; + nxp,pn544-irq = <&tlmm 92 0x00>; + nxp,pn544-ven = <&tlmm 12 0x00>; + nxp,pn544-fw-dwnld = <&tlmm 93 0x00>; + nxp,pn544-clk-gpio = <&pm8998_gpios 21 0x00>; + nxp,pn544-ese-pwr = <&tlmm 37 0x00>; + nfc_voltage_s4-supply = <&pm8998_s4>; + nxp,pn544-wake-up = <&tlmm 116 0x00>; + interrupt-parent = <&tlmm>; + qcom,clk-src = "BBCLK3"; + interrupts = <92 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_enable_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "ref_clk"; + }; +}; + +/*Anderson-Config_UARTPIN_as_GPIO*[*/ +&soc { + tlmm: pinctrl@03400000 { + uart_console_deactive: uart_console_deactive { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-disable; + }; + }; + }; +}; + +&uartblsp2dm1 { + pinctrl-names = "uart_active", "uart_deactive"; + pinctrl-0 = <&uart_console_active>; + pinctrl-1 = <&uart_console_deactive>; +}; +/*Anderson-Config_UARTPIN_as_GPIO*]**/ diff --git a/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi new file mode 100644 index 000000000000..f4cc690856ff --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi @@ -0,0 +1,138 @@ +/* Copyright (c) 2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + dsi_samsung_s6e3fa3_1080p_cmd: qcom,mdss_dsi_samsung_s6e3fa3_1080p_cmd { + qcom,cont-splash-enabled; + qcom,mdss-dsi-panel-name = "samsung s6e3fa3 1080p cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FA3"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <70>; + qcom,mdss-dsi-h-pulse-width = <19>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <4>; + qcom,mdss-dsi-v-front-porch = <18>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 14 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 FC 5A 5A + 39 01 00 00 00 00 02 B0 01 + 39 01 00 00 00 00 03 CC 54 4A + 39 01 00 00 00 00 02 B0 06 + 39 01 00 00 00 00 02 FF 02 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 FC A5 A5 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 FC 5A 5A + 39 01 00 00 00 00 03 F4 00 01 + 39 01 00 00 00 00 03 FC A5 A5 + 15 01 00 00 00 00 02 53 20 + 15 01 00 00 00 00 02 55 00 + 39 01 00 00 00 00 02 57 40 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 28 00 02 28 00 + 05 01 00 00 A0 00 02 10 00]; + qcom,mdss-dsi-post-panel-on-command = [05 01 00 00 00 00 02 29 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1b 06 06 0b 10 06 07 05 03 04 00]; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-init-delay-us = <10000>; + qcom,mdss-dsi-reset-sequence = <1 2>,<0 2>, <1 2>; + /*qcom,mdss-dsi-panel-inverted;*/ + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-tear-check-frame-rate=<6000>; + //qcom,mdss-tear-check-sync-cfg-height = <1932>; /* Height + VBP + VFP + VSW */ + //qcom,mdss-tear-check-sync-init-val= <1920>; /* Height */ + //qcom,mdss-tear-check-sync-threshold-start = <4>; + //qcom,mdss-tear-check-sync-threshold-continue = <4>; + //qcom,mdss-tear-check-start-pos= <1920>; /* Height */ + //qcom,mdss-tear-check-rd-ptr-trigger-intr= <1921>; /* Height + 1 */ + qcom,mdss-dsi-panel-orientation = "180"; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-panel-acl-command = [15 01 00 00 00 00 02 55 00]; + qcom,mdss-dsi-acl-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-acl-ncmds = <0>; + qcom,mdss-dsi-acl-npayload = <1>; + qcom,mdss-dsi-panel-srgb-on-command = [39 01 00 00 00 00 02 57 4c]; + qcom,mdss-dsi-panel-srgb-off-command = [39 01 00 00 00 00 02 57 40]; + qcom,mdss-dsi-srgb-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-hbm-on-command = [15 01 00 00 00 00 02 53 E8]; + qcom,mdss-dsi-panel-hbm-off-command = [15 01 00 00 00 00 02 53 28]; + qcom,mdss-dsi-hbm-command-state = "dsi_hs_mode"; +/******************************************************************************/ +// if you need read the lcd register value you can modify the qcom,mdss-dsi-id-command + qcom,mdss-dsi-read-reg-enable-command = [39 01 00 00 00 00 03 F0 5A 5A]; + qcom,mdss-dsi-read-reg-disable-command = [29 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-packetsizetxcmds-command = [37 01 00 00 00 00 02 07 00]; + qcom,mdss-dsi-readpostxcmds-command = [15 01 00 00 00 00 02 B0 00]; + qcom,mdss-dsi-id-command = [ 06 01 00 00 00 00 01 C8 0A 28]; //read C8 41~50th , 40=0x28 + qcom,mdss-dsi-packetsizetxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-readpostxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-id-command-state = "dsi_lp_mode"; + +/*****************************************************************************/ + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_video.dtsi new file mode 100644 index 000000000000..d2b23adbf1b8 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa3_1080p_video.dtsi @@ -0,0 +1,114 @@ +/* Copyright (c) 2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + dsi_samsung_s6e3fa3_1080p_video: qcom,mdss_dsi_samsung_s6e3fa3_1080p_video { + qcom,cont-splash-enabled; + qcom,mdss-dsi-panel-name = "samsung s6e3fa3 1080p video mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FA3"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <60>; + qcom,mdss-dsi-h-pulse-width = <20>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <8>; + qcom,mdss-dsi-v-front-porch = <6>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 8c 00 02 11 00 + 39 01 00 00 00 00 03 F0 5A 5A + 15 01 00 00 00 00 02 B9 01 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 FC 5A 5A + 39 01 00 00 00 00 03 F4 00 01 + 15 01 00 00 00 00 02 C0 32 + 15 01 00 00 00 00 02 F7 03 + 39 01 00 00 00 00 03 F0 A5 A5 + 39 01 00 00 00 00 03 FC A5 A5 + 15 01 00 00 00 00 02 53 20 + 15 01 00 00 64 00 02 51 00 + 39 01 00 00 00 00 02 57 40 + 05 01 00 00 0A 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 28 00 02 28 00 + 05 01 00 00 A0 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1b 06 06 0b 11 05 07 05 03 04 00]; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + /* + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + //qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + */ + //qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "reg_read"; + qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0A 08]; + qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-status-read-length = <1>; + qcom,mdss-dsi-panel-max-error-count = <2>; + qcom,mdss-dsi-panel-status-value = <0x9c>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-force-clock-lane-hs; + qcom,mdss-dsi-init-delay-us = <10000>; + qcom,mdss-dsi-reset-sequence = <1 20>, <0 10>, <1 100>; + /*qcom,mdss-dsi-panel-inverted;*/ + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-dsi-panel-orientation = "180"; + qcom,mdss-dsi-high-brightness-panel; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi new file mode 100644 index 000000000000..8a55c9595500 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi @@ -0,0 +1,177 @@ +/* Copyright (c) 2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + dsi_samsung_s6e3fa5_1080p_cmd: qcom,mdss_dsi_samsung_s6e3fa5_1080p_cmd { + qcom,cont-splash-enabled; + qcom,mdss-dsi-panel-name = "samsung s6e3fa5 1080p cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FA5"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <70>; + qcom,mdss-dsi-h-pulse-width = <19>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <4>; + qcom,mdss-dsi-v-front-porch = <18>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 14 00 02 11 00 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 B0 04 + 39 01 00 00 00 00 04 B4 06 0C 12 + 39 01 00 00 00 00 03 F0 A5 A5 + 15 01 00 00 00 00 02 53 20 + 15 01 00 00 00 00 02 55 00 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 00 + 39 01 00 00 00 00 03 F0 A5 A5 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 28 00 02 28 00 + 05 01 00 00 A0 00 02 10 00]; + qcom,mdss-dsi-post-panel-on-command = [05 01 00 00 00 00 02 29 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1b 06 06 0b 10 06 07 05 03 04 00]; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <255>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-init-delay-us = <10000>; + qcom,mdss-dsi-reset-sequence = <1 2>,<0 2>, <1 2>; + /*qcom,mdss-dsi-panel-inverted;*/ + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-tear-check-frame-rate=<6000>; + //qcom,mdss-tear-check-sync-cfg-height = <1932>; /* Height + VBP + VFP + VSW */ + //qcom,mdss-tear-check-sync-init-val= <1920>; /* Height */ + // qcom,mdss-tear-check-sync-threshold-start = <4>; + // qcom,mdss-tear-check-sync-threshold-continue = <4>; + // qcom,mdss-tear-check-start-pos= <1920>; /* Height */ + // qcom,mdss-tear-check-rd-ptr-trigger-intr= <1921>; /* Height + 1 */ + + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-panel-acl-command = [15 01 00 00 00 00 02 55 00]; + qcom,mdss-dsi-acl-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-acl-ncmds = <0>; + qcom,mdss-dsi-acl-npayload = <1>; + qcom,mdss-dsi-panel-hbm-on-command = [15 01 00 00 00 00 02 53 E8]; + qcom,mdss-dsi-panel-hbm-off-command = [15 01 00 00 00 00 02 53 28]; + qcom,mdss-dsi-hbm-command-state = "dsi_hs_mode"; + /******************************************************************************/ +// if you need read the lcd register value you can modify the qcom,mdss-dsi-id-command + qcom,mdss-dsi-read-reg-enable-command = [39 01 00 00 00 00 03 F0 5A 5A]; + qcom,mdss-dsi-read-reg-disable-command = [29 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-packetsizetxcmds-command = [37 01 00 00 00 00 02 07 00]; + qcom,mdss-dsi-readpostxcmds-command = [15 01 00 00 00 00 02 B0 00]; + qcom,mdss-dsi-id-command = [ 06 01 00 00 00 00 01 C3 0A 19]; //read C3 25~35th , 25=0x19 + qcom,mdss-dsi-packetsizetxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-readpostxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-id-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-srgb-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 C3 81 + 39 01 00 00 00 00 02 B0 19 + 39 01 00 00 00 00 16 C3 B2 06 05 3C CF 14 07 07 B2 4B E7 C9 BF 0A B9 E5 DA 18 FF F6 D7 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 03 f0 A5 A5]; + qcom,mdss-dsi-panel-srgb-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 00 + 39 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-srgb-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-dci-p3-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 C3 81 + 39 01 00 00 00 00 02 B0 19 + 39 01 00 00 00 00 16 C3 D8 00 00 16 D2 00 08 08 C3 1C F1 CF E8 02 C8 E9 DF 00 FF F6 D7 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 03 f0 A5 A5]; + qcom,mdss-dsi-panel-dci-p3-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 00 + 39 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-panel-orientation = "180"; + qcom,mdss-dsi-dci-p3-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-night-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 02 C3 81 + 39 01 00 00 00 00 02 B0 19 + 39 01 00 00 00 00 16 C3 B2 06 05 3C CF 14 07 07 B2 4B E7 C9 BF 0A B9 E5 DA 18 FD FE FA + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 03 f0 A5 A5]; + qcom,mdss-dsi-panel-night-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 C3 01 + 39 01 00 00 00 00 02 B0 18 + 39 01 00 00 00 00 02 C3 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-night-mode-command-state = "dsi_hs_mode"; + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi new file mode 100644 index 000000000000..6189b6903a78 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi @@ -0,0 +1,196 @@ +/* Copyright (c) 2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + dsi_samsung_s6e3fa6_1080p_cmd: qcom,mdss_dsi_samsung_s6e3fa6_1080p_cmd { + qcom,cont-splash-enabled; + qcom,mdss-dsi-panel-name = "samsung s6e3fa6 1080p cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FA6"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <1920>; + qcom,mdss-dsi-h-front-porch = <120>; + qcom,mdss-dsi-h-back-porch = <70>; + qcom,mdss-dsi-h-pulse-width = <19>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <4>; + qcom,mdss-dsi-v-front-porch = <18>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 14 00 02 11 00 + 15 01 00 00 00 00 02 35 00 + 15 01 00 00 00 00 02 53 20 + 15 01 00 00 00 00 02 55 00 + 05 01 00 00 00 00 02 29 00 + ]; + qcom,mdss-dsi-off-command = [05 01 00 00 28 00 02 28 00 + 05 01 00 00 A0 00 02 10 00]; + qcom,mdss-dsi-post-panel-on-command = [05 01 00 00 00 00 02 29 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 1b 06 06 0b 10 06 07 05 03 04 00]; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-t-clk-post = <0x07>; + qcom,mdss-dsi-t-clk-pre = <0x29>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-init-delay-us = <10000>; + qcom,mdss-dsi-reset-sequence = <1 2>,<0 2>, <1 2>; + /*qcom,mdss-dsi-panel-inverted;*/ + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <122>; + qcom,mdss-tear-check-frame-rate=<6000>; + //qcom,mdss-tear-check-sync-cfg-height = <1932>; /* Height + VBP + VFP + VSW */ + //qcom,mdss-tear-check-sync-init-val= <1920>; /* Height */ + // qcom,mdss-tear-check-sync-threshold-start = <4>; + // qcom,mdss-tear-check-sync-threshold-continue = <4>; + // qcom,mdss-tear-check-start-pos= <1920>; /* Height */ + // qcom,mdss-tear-check-rd-ptr-trigger-intr= <1921>; /* Height + 1 */ + + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-panel-acl-command = [15 01 00 00 00 00 02 55 00]; + qcom,mdss-dsi-acl-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-acl-ncmds = <0>; + qcom,mdss-dsi-acl-npayload = <1>; + qcom,mdss-dsi-panel-hbm-on-command = [15 01 00 00 00 00 02 53 E8]; + qcom,mdss-dsi-panel-hbm-off-command = [15 01 00 00 00 00 02 53 28]; + qcom,mdss-dsi-hbm-command-state = "dsi_hs_mode"; + /******************************************************************************/ +// if you need read the lcd register value you can modify the qcom,mdss-dsi-id-command + qcom,mdss-dsi-read-reg-enable-command = [39 01 00 00 00 00 03 F0 5A 5A]; + qcom,mdss-dsi-read-reg-disable-command = [29 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-packetsizetxcmds-command = [37 01 00 00 00 00 02 07 00]; + qcom,mdss-dsi-readpostxcmds-command = [15 01 00 00 00 00 02 B0 00]; + qcom,mdss-dsi-id-command = [ 06 01 00 00 00 00 01 C3 0A 19]; //read C3 25~35th , 25=0x19 + qcom,mdss-dsi-packetsizetxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-readpostxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-id-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-orientation = "180"; + + qcom,mdss-dsi-panel-srgb-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 80 01 + 39 01 00 00 00 00 02 C3 13 + 39 01 00 00 00 00 02 B0 0E + 39 01 00 00 00 00 16 C3 A0 02 04 3B C7 12 08 07 A8 4B E7 C9 BF 0A B9 E3 DA 18 FF F6 D4 + 39 01 00 00 00 00 03 f0 A5 A5 + + + ]; + qcom,mdss-dsi-panel-srgb-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 80 00 + 39 01 00 00 00 00 02 C3 10 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-srgb-command-state = "dsi_hs_mode"; + + qcom,mdss-dsi-panel-dci-p3-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 80 01 + 39 01 00 00 00 00 02 C3 13 + 39 01 00 00 00 00 02 B0 0E + 39 01 00 00 00 00 16 C3 C6 00 00 16 D2 00 04 08 C3 1C F1 CF E2 02 CE E9 DF 00 FF F6 D4 + 39 01 00 00 00 00 03 f0 A5 A5 + + ]; + qcom,mdss-dsi-panel-dci-p3-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 80 00 + 39 01 00 00 00 00 02 C3 10 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-dci-p3-command-state = "dsi_hs_mode"; +/*****************************************************************************/ + qcom,mdss-dsi-panel-night-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 80 01 + 39 01 00 00 00 00 02 C3 13 + 39 01 00 00 00 00 02 B0 0E + 39 01 00 00 00 00 16 C3 A0 02 04 3B C7 12 08 07 A8 4B E7 C9 BF 0A B9 E3 DA 18 FF FE FA + 39 01 00 00 00 00 03 f0 A5 A5 + + ]; + qcom,mdss-dsi-panel-night-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 80 00 + 39 01 00 00 00 00 02 C3 10 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-night-mode-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-read-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 80 01 + 39 01 00 00 00 00 02 C3 13 + 39 01 00 00 00 00 02 B0 0E + 39 01 00 00 00 00 16 C3 A0 02 04 3B C7 12 08 07 A8 4B E7 C9 BF 0A B9 E3 DA 18 FF FF FF + 39 01 00 00 00 00 03 f0 A5 A5 + + ]; + qcom,mdss-dsi-panel-read-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 80 00 + 39 01 00 00 00 00 02 C3 10 + 39 01 00 00 00 00 03 F0 A5 A5 + + ]; + qcom,mdss-dsi-read-mode-command-state = "dsi_hs_mode"; + + +/**************************************************************************/ + }; +}; diff --git a/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi new file mode 100644 index 000000000000..d68065a2ee20 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-samsung_s6e3fc1_cmd.dtsi @@ -0,0 +1,236 @@ +/* Copyright (c) 2014, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + dsi_samsung_s6e3fc1_cmd: qcom,mdss_dsi_samsung_s6e3fc1_cmd { + qcom,cont-splash-enabled; + qcom,mdss-dsi-panel-name = "samsung s6e3fc1 cmd mode dsi panel"; + qcom,mdss-dsi-panel-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-version = "S6E3FC1"; + qcom,mdss-dsi-backlight-version = "SAMSUNG"; + qcom,mdss-dsi-backlight-manufacture = "SAMSUNG"; + qcom,mdss-dsi-panel-controller = <&mdss_dsi0>; + qcom,mdss-dsi-panel-type = "dsi_cmd_mode"; + qcom,mdss-dsi-panel-destination = "display_1"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2160>; + qcom,mdss-dsi-h-front-porch = <128>; + qcom,mdss-dsi-h-back-porch = <64>; + qcom,mdss-dsi-h-pulse-width = <16>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <4>; + qcom,mdss-dsi-v-front-porch = <18>; + qcom,mdss-dsi-v-pulse-width = <2>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 05 01 00 00 19 00 02 11 00 + 15 01 00 00 00 00 02 35 00 + 39 01 00 00 00 00 03 FC 5A 5A + 39 01 00 00 00 00 04 E8 64 08 0C + 39 01 00 00 00 00 03 FC A5 A5 + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 02 B0 01 + 39 01 00 00 00 00 02 ED 04 + 39 01 00 00 00 00 03 F0 A5 A5 + 15 01 00 00 00 00 02 53 20 + 15 01 00 00 00 00 02 55 00 + 05 01 00 00 00 00 02 29 00 + ]; + + qcom,mdss-dsi-off-command = [05 01 00 00 0A 00 02 28 00 + 05 01 00 00 A0 00 02 10 00]; + qcom,mdss-dsi-post-panel-on-command = [05 01 00 00 00 00 02 29 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <1>; + qcom,mdss-dsi-traffic-mode = "burst_mode"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-panel-timings = [00 20 07 07 0c 12 06 08 06 03 04 00]; + qcom,mdss-dsi-te-pin-select = <1>; + qcom,mdss-dsi-te-v-sync-rd-ptr-irq-line = <0x2c>; + qcom,mdss-dsi-te-v-sync-continue-lines = <0x3c>; + qcom,mdss-dsi-te-dcs-command = <1>; + qcom,esd-check-enabled; + qcom,mdss-dsi-panel-status-check-mode = "te_signal_check"; + qcom,mdss-dsi-te-check-enable; + qcom,mdss-dsi-te-using-te-pin; + qcom,mdss-dsi-t-clk-post = <0x08>; + qcom,mdss-dsi-t-clk-pre = <0x30>; + qcom,mdss-dsi-bl-min-level = <1>; + qcom,mdss-dsi-bl-max-level = <1023>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "trigger_sw"; + qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-init-delay-us = <10000>; + qcom,mdss-dsi-reset-sequence = <1 2>,<0 2>, <1 2>; + /*qcom,mdss-dsi-panel-inverted;*/ + qcom,mdss-pan-physical-width-dimension = <68>; + qcom,mdss-pan-physical-height-dimension = <137>; + qcom,mdss-tear-check-frame-rate=<6000>; + /* Height + VBP + VFP + VSW */ + //qcom,mdss-tear-check-sync-cfg-height = <1932>; + //qcom,mdss-tear-check-sync-init-val= <1920>; /* Height */ + //qcom,mdss-tear-check-sync-threshold-start = <4>; + //qcom,mdss-tear-check-sync-threshold-continue = <4>; + //qcom,mdss-tear-check-start-pos= <1920>; /* Height */ + //qcom,mdss-tear-check-rd-ptr-trigger-intr= <1921>; /* Height + 1 */ + qcom,mdss-brightness-max-level = <1023>; + qcom,mdss-dsi-high-brightness-panel; + qcom,mdss-dsi-panel-acl-command = [15 01 00 00 00 00 02 55 00]; + qcom,mdss-dsi-acl-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-acl-ncmds = <0>; + qcom,mdss-dsi-acl-npayload = <1>; + qcom,mdss-dsi-panel-hbm-on-command = [15 01 00 00 00 00 02 53 E8]; + qcom,mdss-dsi-panel-hbm-off-command = [15 01 00 00 00 00 02 53 28]; + qcom,mdss-dsi-hbm-command-state = "dsi_hs_mode"; + /**************************************/ + //if you need read the lcd register value + //you can modify the qcom,mdss-dsi-id-command + qcom,mdss-dsi-read-reg-enable-command = [39 01 00 00 00 00 03 F0 5A 5A]; + qcom,mdss-dsi-read-reg-disable-command = [29 01 00 00 00 00 03 F0 A5 A5]; + qcom,mdss-dsi-packetsizetxcmds-command = [37 01 00 00 00 00 02 07 00]; + qcom,mdss-dsi-readpostxcmds-command = [15 01 00 00 00 00 02 B0 00]; + //read C3 25~35th , 25=0x19 + qcom,mdss-dsi-id-command = [ 06 01 00 00 00 00 01 C3 0A 19]; + qcom,mdss-dsi-packetsizetxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-readpostxcmds-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-enable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-read-reg-disable-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-id-command-state = "dsi_lp_mode"; + qcom,mdss-bl-high2bit; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-panel-seria-num-command = [06 01 00 00 00 00 01 A1]; + qcom,mdss-dsi-panel-seria-num-state = "dsi_lp_mode"; + qcom,mdss-dsi-panel-seria-num-year-index = <12>; + qcom,mdss-dsi-panel-seria-num-mon-index = <12>; + qcom,mdss-dsi-panel-seria-num-day-index = <13>; + qcom,mdss-dsi-panel-seria-num-hour-index = <14>; + qcom,mdss-dsi-panel-seria-num-min-index = <15>; + + qcom,mdss-dsi-panel-srgb-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 BC 01 + 39 01 00 00 00 00 02 B0 01 + 39 01 00 00 00 00 02 BC 12 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 bc A3 05 04 46 cd 10 05 09 b0 57 ef cf bb 11 bf e1 da 17 ff f9 d8 + 39 01 00 00 00 00 02 b0 42 + 39 01 00 00 00 00 02 bc 03 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 bc A1 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-srgb-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 BC E0 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-srgb-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-dci-p3-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 02 BC 01 + 39 01 00 00 00 00 02 B0 01 + 39 01 00 00 00 00 02 BC 12 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 BC C6 00 00 1e cf 00 06 0a c3 26 ef cd e0 04 ce e9 df 00 ff f9 d8 + 39 01 00 00 00 00 02 b0 42 + 39 01 00 00 00 00 02 bc 03 + 39 01 00 00 00 00 02 b0 4B + 39 01 00 00 00 00 02 bc A1 + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-dci-p3-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 BC E0 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-dci-p3-command-state = "dsi_hs_mode"; +/*****************************************************************************/ + qcom,mdss-dsi-panel-night-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5A 5A + 39 01 00 00 00 00 03 BC 01 12 + 39 01 00 00 00 00 02 B0 2C + 39 01 00 00 00 00 16 BC A0 02 04 3B C7 12 08 07 A8 4B E7 C9 BF 0A B9 E3 DA 18 FF FE FA + 39 01 00 00 00 00 03 f0 A5 A5 + ]; + qcom,mdss-dsi-panel-night-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 BC E0 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-night-mode-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-oneplus-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 03 bc 01 12 + 39 01 00 00 00 00 02 b0 2c + 39 01 00 00 00 00 16 bc b4 02 04 05 ff 02 00 00 ff 00 ff ff f0 00 f0 e0 e1 18 ff fe fB + 39 01 00 00 00 00 03 f0 a5 a5 + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 02 bc 01 + 39 01 00 00 00 00 02 b0 42 + 39 01 00 00 00 00 02 bc 03 + 39 01 00 00 00 00 02 b0 4b + 39 01 00 00 00 00 02 bc 01 + 39 01 00 00 00 00 03 f0 a5 a5 + ]; + qcom,mdss-dsi-panel-oneplus-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 BC E0 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-read-mode-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-panel-adaption-mode-on-command = [ + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 03 bc 01 12 + 39 01 00 00 00 00 02 b0 2c + 39 01 00 00 00 00 16 bc B8 03 04 45 E2 10 04 07 C1 4B EB D7 B8 0A BF FF ED 14 FF FF FA + 39 01 00 00 00 00 03 f0 a5 a5 + 39 01 00 00 00 00 03 f0 5a 5a + 39 01 00 00 00 00 02 bc 01 + 39 01 00 00 00 00 02 b0 42 + 39 01 00 00 00 00 02 bc 03 + 39 01 00 00 00 00 02 b0 4b + 39 01 00 00 00 00 02 bc 01 + 39 01 00 00 00 00 03 f0 a5 a5 + ]; + qcom,mdss-dsi-panel-adaption-mode-off-command = [ + 39 01 00 00 00 00 03 F0 5A 5A + 39 01 00 00 00 00 03 BC E0 00 + 39 01 00 00 00 00 03 F0 A5 A5 + ]; + qcom,mdss-dsi-adaption-mode-command-state = "dsi_hs_mode"; + +/**************************************************************************/ + }; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-Second_Resource.dtsi b/arch/arm/boot/dts/qcom/dumpling-Second_Resource.dtsi new file mode 100644 index 000000000000..1917ac747ce1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-Second_Resource.dtsi @@ -0,0 +1,1114 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fc1_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fc1_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,platform-poc-gpio = <&tlmm 66 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + +//camera +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: + cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: + cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: + cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* +* gpios = <&tlmm 27 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +8 qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398"; + // eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350"; + // eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371"; + // eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-dvt.dtsi b/arch/arm/boot/dts/qcom/dumpling-dvt.dtsi new file mode 100644 index 000000000000..1917ac747ce1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-dvt.dtsi @@ -0,0 +1,1114 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fc1_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fc1_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,platform-poc-gpio = <&tlmm 66 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + +//camera +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: + cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: + cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: + cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* +* gpios = <&tlmm 27 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +8 qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398"; + // eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350"; + // eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371"; + // eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-pvt.dtsi b/arch/arm/boot/dts/qcom/dumpling-pvt.dtsi new file mode 100644 index 000000000000..1917ac747ce1 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-pvt.dtsi @@ -0,0 +1,1114 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dsi-panel-samsung_s6e3fa3_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa3_1080p_video.dtsi" +#include "dsi-panel-samsung_s6e3fa5_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fa6_1080p_cmd.dtsi" +#include "dsi-panel-samsung_s6e3fc1_cmd.dtsi" + +&mdss_dsi_active { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi_suspend { + mux { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; + config { + pins = "gpio94", "gpio21", "gpio62", "gpio66"; + }; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_samsung_s6e3fc1_cmd>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 94 0>; + qcom,platform-te-gpio = <&tlmm 11 0>; + qcom,platform-vci-gpio = <&tlmm 21 0>; + qcom,platform-poc-gpio = <&tlmm 66 0>; + qcom,panel-mode-gpio = <>; + lab-supply = <>; + ibb-supply = <>; + clocks = <&clock_mmss clk_mmss_mdss_byte0_clk>, + <&clock_mmss clk_mmss_mdss_pclk0_clk>, + <&clock_mmss clk_mmss_mdss_esc0_clk>, + <&clock_mmss clk_byte0_clk_src>, + <&clock_mmss clk_pclk0_clk_src>, + <&clock_mmss clk_mmss_mdss_byte0_intf_clk>; + clock-names = "byte_clk", "pixel_clk", "core_clk", + "byte_clk_rcg", "pixel_clk_rcg", + "byte_intf_clk"; +}; + +&dsi_samsung_s6e3fa3_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa3_1080p_video { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa5_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fa6_1080p_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + +&dsi_samsung_s6e3fc1_cmd { + qcom,panel-supply-entries = <&dsi_panel_pwr_supply_no_labibb>; +}; + + +//camera +&soc { + tlmm: pinctrl@03400000 { + cam_sensor_rear_0_mclk_active: cam_sensor_rear_0_mclk_active { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_mclk_suspend: cam_sensor_rear_0_mclk_suspend { + /* MCLK0 */ + mux { + /* CLK, DATA */ + pins = "gpio13"; + function = "cam_mclk"; + }; + + config { + pins = "gpio13"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_active: cam_sensor_rear_0_active { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_suspend: cam_sensor_rear_0_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio30","gpio7"; + function = "gpio"; + }; + + config { + pins = "gpio30","gpio7"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_active: cam_sensor_rear_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_ana_suspend: cam_sensor_rear_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio27"; + function = "gpio"; + }; + + config { + pins = "gpio27"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_active: + cam_sensor_rear_0_actuator_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_0_actuator_vaf_suspend: + cam_sensor_rear_0_actuator_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio6"; + function = "gpio"; + }; + + config { + pins = "gpio6"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_active: cam_sensor_rear_1_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_mclk_suspend: cam_sensor_rear_1_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio15"; + function = "cam_mclk"; + }; + + config { + pins = "gpio15"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_active: cam_sensor_rear_1_active { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_suspend: cam_sensor_rear_1_suspend { + /* RESET, STANDBY */ + mux { + pins = "gpio9","gpio8"; + function = "gpio"; + }; + config { + pins = "gpio9","gpio8"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_active: cam_sensor_rear_1_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_ana_suspend: cam_sensor_rear_1_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio23"; + function = "gpio"; + }; + + config { + pins = "gpio23"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_active: cam_sensor_rear_1_vaf_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_rear_1_vaf_suspend: cam_sensor_rear_1_vaf_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio77"; + function = "gpio"; + }; + + config { + pins = "gpio77"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_active: cam_sensor_front_0_mclk_active { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_mclk_suspend: + cam_sensor_front_0_mclk_suspend { + /* MCLK1 */ + mux { + /* CLK, DATA */ + pins = "gpio14"; + function = "cam_mclk"; + }; + + config { + pins = "gpio14"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_active: cam_sensor_front_0_active { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_suspend: cam_sensor_front_0_suspend { + /* RESET, VDIG_EN */ + mux { + pins = "gpio28", "gpio78"; + function = "gpio"; + }; + + config { + pins = "gpio28", "gpio78"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_active: cam_sensor_front_0_ana_active { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-disable; /* No PULL */ + drive-strength = <2>; /* 2 MA */ + }; + }; + + cam_sensor_front_0_ana_suspend: cam_sensor_front_0_ana_suspend { + /* ACTUATOR POWER */ + mux { + pins = "gpio85"; + function = "gpio"; + }; + + config { + pins = "gpio85"; + bias-pull-down; /* PULL DOWN */ + drive-strength = <2>; /* 2 MA */ + }; + }; + }; + + led_flash0: qcom,camera-flash@0 { + cell-index = <0>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash0 &pmi8998_flash1>; + qcom,torch-source = <&pmi8998_torch0 &pmi8998_torch1>; + qcom,switch-source = <&pmi8998_switch0>; + status = "ok"; + }; + + led_flash1: qcom,camera-flash@1 { + cell-index = <1>; + compatible = "qcom,camera-flash"; + qcom,flash-source = <&pmi8998_flash2>; + qcom,torch-source = <&pmi8998_torch2>; + qcom,switch-source = <&pmi8998_switch1>; + status = "disabled"; + }; +}; + +&cci { + actuator0: qcom,actuator@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,actuator"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + actuator1: qcom,actuator@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,actuator"; + qcom,cci-master = <1>; +/* +* gpios = <&tlmm 27 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +8 qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_vaf_suspend>; + status = "ok"; + }; + + ois0: qcom,ois@0 { + cell-index = <0>; + reg = <0x0>; + compatible = "qcom,ois"; + qcom,cci-master = <0>; +/* +* gpios = <&tlmm 6 0>; +* qcom,gpio-vaf = <0>; +* qcom,gpio-req-tbl-num = <0>; +* qcom,gpio-req-tbl-flags = <0>; +* qcom,gpio-req-tbl-label = "CAM_VAF"; +*/ + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_actuator_vaf_suspend>; + status = "ok"; + }; + + eeprom0: qcom,eeprom@0 { + cell-index = <0>; + reg = <0>; + compatible = "qcom,eeprom"; +// qcom,eeprom-name = "sony_imx398"; + // eeprom will be probed in userspace + position = <0x00>; + qcom,slave-addr = <0xa0>; + qcom,cci-master = <0>; + qcom,i2c-freq-mode = <1>; + qcom,num-blocks = <9>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa0>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa0>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa0>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa0>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa0>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa0>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa0>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa0>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa0>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_active>; + pinctrl-1 = <&cam_sensor_rear_suspend>; + gpios = <&tlmm 30 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom1: qcom,eeprom@1 { + cell-index = <1>; + reg = <0x1>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx350"; + // eeprom will be probed in userspace + position = <0x01>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <1>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_active>; + pinctrl-1 = <&cam_sensor_rear_1_suspend>; + gpios = <&tlmm 9 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + eeprom2: qcom,eeprom@2 { + cell-index = <2>; + reg = <0x2>; + compatible = "qcom,eeprom"; + //qcom,eeprom-name = "sony_imx371"; + // eeprom will be probed in userspace + position = <0x02>; + qcom,slave-addr = <0xa2>; + qcom,cci-master = <0>; + qcom,num-blocks = <24>; + qcom,i2c-freq-mode = <1>; + + qcom,page0 = <0 0x0000 2 0x00 1 5>; + qcom,pageen0 = <0 0x0000 2 0x00 1 5>; + qcom,poll0 = <0 0x0000 2 0x00 1 5>; + qcom,saddr0 = <0xa2>; + qcom,mem0 = <0x0100 0x0000 2 0 1 0>; + + qcom,page1 = <0 0x0000 2 0x00 1 5>; + qcom,pageen1 = <0 0x0000 2 0x00 1 5>; + qcom,poll1 = <0 0x0000 2 0x00 1 5>; + qcom,saddr1 = <0xa2>; + qcom,mem1 = <0x0100 0x0100 2 0 1 0>; + + qcom,page2 = <0 0x0000 2 0x00 1 5>; + qcom,pageen2 = <0 0x0000 2 0x00 1 5>; + qcom,poll2 = <0 0x0000 2 0x00 1 5>; + qcom,saddr2 = <0xa2>; + qcom,mem2 = <0x0100 0x0200 2 0 1 0>; + + qcom,page3 = <0 0x0000 2 0x00 1 5>; + qcom,pageen3 = <0 0x0000 2 0x00 1 5>; + qcom,poll3 = <0 0x0000 2 0x00 1 5>; + qcom,saddr3 = <0xa2>; + qcom,mem3 = <0x0100 0x0300 2 0 1 0>; + + qcom,page4 = <0 0x0000 2 0x00 1 5>; + qcom,pageen4 = <0 0x0000 2 0x00 1 5>; + qcom,poll4 = <0 0x0000 2 0x00 1 5>; + qcom,saddr4 = <0xa2>; + qcom,mem4 = <0x0100 0x0400 2 0 1 0>; + + qcom,page5 = <0 0x0000 2 0x00 1 5>; + qcom,pageen5 = <0 0x0000 2 0x00 1 5>; + qcom,poll5 = <0 0x0000 2 0x00 1 5>; + qcom,saddr5 = <0xa2>; + qcom,mem5 = <0x0100 0x0500 2 0 1 0>; + + qcom,page6 = <0 0x0000 2 0x00 1 5>; + qcom,pageen6 = <0 0x0000 2 0x00 1 5>; + qcom,poll6 = <0 0x0000 2 0x00 1 5>; + qcom,saddr6 = <0xa2>; + qcom,mem6 = <0x0100 0x0600 2 0 1 0>; + + qcom,page7 = <0 0x0000 2 0x00 1 5>; + qcom,pageen7 = <0 0x0000 2 0x00 1 5>; + qcom,poll7 = <0 0x0000 2 0x00 1 5>; + qcom,saddr7 = <0xa2>; + qcom,mem7 = <0x0100 0x0700 2 0 1 0>; + + qcom,page8 = <0 0x0000 2 0x00 1 5>; + qcom,pageen8 = <0 0x0000 2 0x00 1 5>; + qcom,poll8 = <0 0x0000 2 0x00 1 5>; + qcom,saddr8 = <0xa2>; + qcom,mem8 = <0x0100 0x0800 2 0 1 0>; + + qcom,page9 = <0 0x0000 2 0x00 1 5>; + qcom,pageen9 = <0 0x0000 2 0x00 1 5>; + qcom,poll9 = <0 0x0000 2 0x00 1 5>; + qcom,saddr9 = <0xa2>; + qcom,mem9 = <0x0100 0x0900 2 0 1 0>; + + qcom,page10 = <0 0x0000 2 0x00 1 5>; + qcom,pageen10 = <0 0x0000 2 0x00 1 5>; + qcom,poll10 = <0 0x0000 2 0x00 1 5>; + qcom,saddr10 = <0xa2>; + qcom,mem10 = <0x0100 0x0A00 2 0 1 0>; + + qcom,page11 = <0 0x0000 2 0x00 1 5>; + qcom,pageen11 = <0 0x0000 2 0x00 1 5>; + qcom,poll11 = <0 0x0000 2 0x00 1 5>; + qcom,saddr11 = <0xa2>; + qcom,mem11 = <0x0100 0x0B00 2 0 1 0>; + + qcom,page12 = <0 0x0000 2 0x00 1 5>; + qcom,pageen12 = <0 0x0000 2 0x00 1 5>; + qcom,poll12 = <0 0x0000 2 0x00 1 5>; + qcom,saddr12 = <0xa2>; + qcom,mem12 = <0x0100 0x0C00 2 0 1 0>; + + qcom,page13 = <0 0x0000 2 0x00 1 5>; + qcom,pageen13 = <0 0x0000 2 0x00 1 5>; + qcom,poll13 = <0 0x0000 2 0x00 1 5>; + qcom,saddr13 = <0xa2>; + qcom,mem13 = <0x0100 0x0D00 2 0 1 0>; + + qcom,page14 = <0 0x0000 2 0x00 1 5>; + qcom,pageen14 = <0 0x0000 2 0x00 1 5>; + qcom,poll14 = <0 0x0000 2 0x00 1 5>; + qcom,saddr14 = <0xa2>; + qcom,mem14 = <0x0100 0x0E00 2 0 1 0>; + + qcom,page15 = <0 0x0000 2 0x00 1 5>; + qcom,pageen15 = <0 0x0000 2 0x00 1 5>; + qcom,poll15 = <0 0x0000 2 0x00 1 5>; + qcom,saddr15 = <0xa2>; + qcom,mem15 = <0x0100 0x0F00 2 0 1 0>; + + qcom,page16 = <0 0x0000 2 0x00 1 5>; + qcom,pageen16 = <0 0x0000 2 0x00 1 5>; + qcom,poll16 = <0 0x0000 2 0x00 1 5>; + qcom,saddr16 = <0xa2>; + qcom,mem16 = <0x0100 0x1000 2 0 1 0>; + + qcom,page17 = <0 0x0000 2 0x00 1 5>; + qcom,pageen17 = <0 0x0000 2 0x00 1 5>; + qcom,poll17 = <0 0x0000 2 0x00 1 5>; + qcom,saddr17 = <0xa2>; + qcom,mem17 = <0x0100 0x1100 2 0 1 0>; + + qcom,page18 = <0 0x0000 2 0x00 1 5>; + qcom,pageen18 = <0 0x0000 2 0x00 1 5>; + qcom,poll18 = <0 0x0000 2 0x00 1 5>; + qcom,saddr18 = <0xa2>; + qcom,mem18 = <0x0100 0x1200 2 0 1 0>; + + qcom,page19 = <0 0x0000 2 0x00 1 5>; + qcom,pageen19 = <0 0x0000 2 0x00 1 5>; + qcom,poll19 = <0 0x0000 2 0x00 1 5>; + qcom,saddr19 = <0xa2>; + qcom,mem19 = <0x0100 0x1300 2 0 1 0>; + + qcom,page20 = <0 0x0000 2 0x00 1 5>; + qcom,pageen20 = <0 0x0000 2 0x00 1 5>; + qcom,poll20 = <0 0x0000 2 0x00 1 5>; + qcom,saddr20 = <0xa2>; + qcom,mem20 = <0x0100 0x1400 2 0 1 0>; + + qcom,page21 = <0 0x0000 2 0x00 1 5>; + qcom,pageen21 = <0 0x0000 2 0x00 1 5>; + qcom,poll21 = <0 0x0000 2 0x00 1 5>; + qcom,saddr21 = <0xa2>; + qcom,mem21 = <0x0100 0x1500 2 0 1 0>; + + qcom,page22 = <0 0x0000 2 0x00 1 5>; + qcom,pageen22 = <0 0x0000 2 0x00 1 5>; + qcom,poll22 = <0 0x0000 2 0x00 1 5>; + qcom,saddr22 = <0xa2>; + qcom,mem22 = <0x0100 0x1600 2 0 1 0>; + + qcom,page23= <0 0x0000 2 0x00 1 5>; + qcom,pageen23 = <0 0x0000 2 0x00 1 5>; + qcom,poll23 = <0 0x0000 2 0x00 1 5>; + qcom,saddr23 = <0xa2>; + qcom,mem23 = <0x0002 0x1700 2 0 1 0>; + + cam_vio-supply = <&pm8998_lvs1>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-min-voltage = <0>; + qcom,cam-vreg-max-voltage = <0>; + qcom,cam-vreg-op-mode = <0>; + + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_active>; + pinctrl-1 = <&cam_sensor_front_0_suspend>; + gpios = <&tlmm 28 0>; + qcom,gpio-vio = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_VIO"; + + qcom,cam-power-seq-type = "sensor_vreg"; + qcom,cam-power-seq-val = "cam_vio"; + qcom,cam-power-seq-cfg-val = <1>; + qcom,cam-power-seq-delay = <1>; + + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + status = "ok"; + }; + + qcom,camera@0 { + cell-index = <0>; + compatible = "qcom,camera"; + reg = <0x0>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,mount-angle = <270>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator0>; + qcom,eeprom-src = <&eeprom0>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pm8998_l22>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 2864000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 2864000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 2864000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_0_mclk_active + &cam_sensor_rear_0_active + &cam_sensor_rear_0_ana_active + &cam_sensor_rear_0_actuator_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_0_mclk_suspend + &cam_sensor_rear_0_suspend + &cam_sensor_rear_0_ana_suspend + &cam_sensor_rear_0_actuator_vaf_suspend>; + gpios = <&tlmm 13 0>, // mclk + <&tlmm 30 0>, // reset + <&tlmm 7 0>, // digital enable + <&tlmm 27 0>, // ana enable + <&tlmm 6 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_0", + "CAM_RESET_0", + "CAM_VDIG_0", + "CAM_VANA_0", + "CAM_AF_EN_0"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk0_clk_src>, + <&clock_mmss clk_mmss_camss_mclk0_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@1 { + cell-index = <1>; + compatible = "qcom,camera"; + reg = <0x1>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <90>; + qcom,led-flash-src = <&led_flash0>; + qcom,actuator-src = <&actuator1>; + qcom,eeprom-src = <&eeprom1>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vaf-supply = <&pmi8998_bob>; + cam_v_custom1-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", + "cam_vana", "cam_vaf", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_rear_1_mclk_active + &cam_sensor_rear_1_active + &cam_sensor_rear_1_ana_active + &cam_sensor_rear_1_vaf_active>; + pinctrl-1 = <&cam_sensor_rear_1_mclk_suspend + &cam_sensor_rear_1_suspend + &cam_sensor_rear_1_ana_suspend + &cam_sensor_rear_1_vaf_suspend>; + gpios = <&tlmm 15 0>, // mclk + <&tlmm 9 0>, // reset + <&tlmm 8 0>, // digital enable + <&tlmm 23 0>, // ana enable + <&tlmm 77 0>; // AF enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-vaf = <4>; + qcom,gpio-req-tbl-num = <0 1 2 3 4>; + qcom,gpio-req-tbl-flags = <1 0 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_1", + "CAM_RESET_1", + "CAM_VDIG_1", + "CAM_VANA_1", + "CAM_AF_EN_1"; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + qcom,cci-master = <1>; + status = "ok"; + clocks = <&clock_mmss clk_mclk2_clk_src>, + <&clock_mmss clk_mmss_camss_mclk2_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; + + qcom,camera@2 { + cell-index = <2>; + compatible = "qcom,camera"; + reg = <0x02>; + qcom,csiphy-sd-index = <2>; + qcom,csid-sd-index = <2>; + qcom,mount-angle = <90>; + qcom,eeprom-src = <&eeprom2>; + cam_vio-supply = <&pm8998_lvs1>; + cam_vana-supply = <&pmi8998_bob>; + cam_vdig-supply = <&pm8998_s3>; + qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig"; + qcom,cam-vreg-min-voltage = <0 3312000 1352000>; + qcom,cam-vreg-max-voltage = <0 3600000 1352000>; + qcom,cam-vreg-op-mode = <0 3600000 1352000>; + qcom,gpio-no-mux = <0>; + pinctrl-names = "cam_default", "cam_suspend"; + pinctrl-0 = <&cam_sensor_front_0_mclk_active + &cam_sensor_front_0_active + &cam_sensor_front_0_ana_active>; + pinctrl-1 = <&cam_sensor_front_0_mclk_suspend + &cam_sensor_front_0_suspend + &cam_sensor_front_0_ana_suspend>; + gpios = <&tlmm 14 0>,// mclk + <&tlmm 28 0>,// reset + <&tlmm 78 0>,// vdig enable + <&tlmm 85 0>;// vana enable + qcom,gpio-reset = <1>; + qcom,gpio-vdig = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK_2", + "CAM_RESET_2", + "CAM_VDIG_2", + "CAM_VANA_2"; + qcom,sensor-position = <1>; + qcom,sensor-mode = <0>; + qcom,cci-master = <0>; + status = "ok"; + clocks = <&clock_mmss clk_mclk1_clk_src>, + <&clock_mmss clk_mmss_camss_mclk1_clk>; + clock-names = "cam_src_clk", "cam_clk"; + qcom,clock-rates = <24000000 0>; + }; +}; +&pm8998_gpios { + gpio@c800 { /* GPIO 9 - CAMERA SENSOR 2 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <0>; /* VIN1 GPIO_LV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; + + gpio@d300 { /* GPIO 20 - CAMERA SENSOR 0 VDIG */ + qcom,mode = <1>; /* Output */ + qcom,pull = <5>; /* No Pull */ + qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ + qcom,src-sel = <0>; /* GPIO */ + qcom,invert = <0>; /* Invert */ + qcom,master-en = <1>; /* Enable GPIO */ + status = "ok"; + }; +}; +&wsa_spkr_sd2 { + pinctrl-0 = <>; + pinctrl-1 = <>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-v2.1-Second_Resource.dts b/arch/arm/boot/dts/qcom/dumpling-v2.1-Second_Resource.dts new file mode 100644 index 000000000000..8c6a1d1dc332 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-v2.1-Second_Resource.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "dumpling.dtsi" +#include "dumpling-Second_Resource.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 17801 53>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-v2.1-dvt.dts b/arch/arm/boot/dts/qcom/dumpling-v2.1-dvt.dts new file mode 100644 index 000000000000..3a88b04990ec --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-v2.1-dvt.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "dumpling.dtsi" +#include "dumpling-dvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 17801 42>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling-v2.1-pvt.dts b/arch/arm/boot/dts/qcom/dumpling-v2.1-pvt.dts new file mode 100644 index 000000000000..6ed50d4ee4c7 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling-v2.1-pvt.dts @@ -0,0 +1,26 @@ +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/dts-v1/; + +#include "msm8998-v2.1.dtsi" +#include "msm8998-mdss-panels.dtsi" +#include "msm8998-mtp.dtsi" +#include "dumpling.dtsi" +#include "dumpling-pvt.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. MSM 8998 v2.1 MTP"; + compatible = "qcom,msm8998-mtp", "qcom,msm8998", "qcom,mtp"; + qcom,board-id = <8 0 17801 43>; +}; diff --git a/arch/arm/boot/dts/qcom/dumpling.dtsi b/arch/arm/boot/dts/qcom/dumpling.dtsi new file mode 100755 index 000000000000..30163473d9c5 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dumpling.dtsi @@ -0,0 +1,1052 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "pxlw-iris-settings.dtsi" +/ { + reserved-memory { + ramoops_mem: ramoops_mem@ac000000 { + reg = <0 0xAC000000 0 0x00200000>; + label = "ramoops_mem"; + }; + param_mem: param_mem@ac200000 { + reg = <0 0xAC200000 0 0x00100000>; + label = "param_mem"; + }; + mtp_mem: mtp_mem@ac400000 { + reg = <0 0xAC400000 0 0x00B00000>; + label = "mtp_mem"; + }; + bootloader_log_mem: bootloader_log_mem@9fff7000 { + reg = <0 0x9FFF7000 0 0x00100000>; + label = "bootloader_log_mem"; + }; + }; +}; + +&pil_ipa_gpu_mem{ + reg = <0 0x95600000 0 0x100000>;//move start address +4M to match modem aligned +}; + +&pil_slpi_mem{ + reg = <0 0x94700000 0 0xf00000>;//move start address +4M to match modem aligned +}; + +&pil_mba_mem{ + reg = <0 0x94500000 0 0x200000>;//move start address +4M to match modem aligned +}; + +&pil_video_mem{ + reg = <0 0x94000000 0 0x500000>;//move start address +4M to match modem aligned +}; + +&modem_mem{ + reg = <0 0x8d000000 0 0x7000000>;//move start address +4M to match modem aligned +}; + +&pil_adsp_mem{ + reg = <0 0x8b200000 0 0x1e00000>; //increae 4M size by oneplus +}; + +/* I/O */ +&soc { + gpio_keys { + vol_up { + /delete-property/ gpio-key,wakeup; + }; + vol_down { + label = "volume_down"; + gpios = <&pm8998_gpios 5 0x1>; + linux,input-type = <1>; + linux,code = <114>; + debounce-interval = <15>; + }; + hallsensor_key { + label = "hallsensor_key"; + gpios = <&tlmm 124 1>; + interrupt-parent = <&tlmm>; + interrupts = <124 0x0>; + linux,input-type = <5>; + linux,code = <0>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; + + tlmm: pinctrl@03400000 { + tri_state_key_mux { + tri_state_key_active: tri_state_key_active { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + + tri_state_key_suspend: tri_state_key_suspend { + mux { + pins = "gpio42","gpio26","gpio40"; + function = "gpio"; + }; + + config { + pins = "gpio42","gpio26","gpio40"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + + oem_rf_cable_mux { + oem_rf_cable_active: oem_rf_cable_active { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + oem_rf_cable_suspend: oem_rf_cable_suspend { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; + + }; + }; + }; + + /* Tri-state key */ + tri_state_key { + compatible = "oneplus,tri-state-key"; + status = "okay"; + interrupt-parent = <&tlmm>; + tristate,gpio_key1 = <&tlmm 40 0x00>; + tristate,gpio_key2 = <&tlmm 42 0x00>; + tristate,gpio_key3 = <&tlmm 26 0x00>; + + pinctrl-names = "pmx_tri_state_key_active", + "pmx_tri_state_key_suspend"; + pinctrl-0 = <&tri_state_key_active>; + pinctrl-1 = <&tri_state_key_suspend>; + }; + + oem_rf_cable { + compatible = "oem,rf_cable"; + interrupt-parent = <&tlmm>; + rf,cable-gpio = <&tlmm 36 0>; + pinctrl-names = "oem_rf_cable_active", + "oem_rf_cable_suspend"; + pinctrl-0 = <&oem_rf_cable_active >; + pinctrl-1 = <&oem_rf_cable_suspend>; + }; + + qcom,qbt1000 { + status = "disabled"; + }; + + /* Fingerprint */ + fpc_fpc1020 { + compatible = "fpc,fpc1020"; + interrupt-parent = <&tlmm>; + fpc,irq-gpio = <&tlmm 121 0>; + /* + *fpc,gpio_id0 = <&tlmm 39 0>; + *fpc,gpio_id1 = <&tlmm 41 0>; + *fpc,gpio_id2 = <&tlmm 63 0>; + */ + oem,dumpling; + }; + + tlmm: pinctrl@03400000 { + goodixfp_enable_init: goodixfp_enable_init { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; + bias-pull-up; + output-enable; + output-high; + }; + }; + goodixfp_disable_init: goodixfp_disable_init { + mux { + pins = "gpio65"; + function = "gpio"; + }; + + config { + pins = "gpio65"; + drive-strength = <8>; + bias-pull-up; + output-enable; + output-low; + }; + }; + goodixfp_irq_init: goodixfp_irq_init { + mux { + pins = "gpio121"; + function = "gpio"; + }; + + config { + pins = "gpio121"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + }; + + goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + fp-gpio-irq = <&tlmm 121 0x00>; + fp-gpio-reset = <&tlmm 75 0x00>; + fp-gpio-enable = <&tlmm 65 0x00>; + pinctrl-names = "fp_en_init", "fp_dis_init"; + pinctrl-0 = <&goodixfp_enable_init &goodixfp_irq_init>; + pinctrl-1 = <&goodixfp_disable_init>; + status = "okay"; + oem,dumpling; + }; + + tlmm: pinctrl@03400000 { + fp_enable: fp_enable { + mux { + pins = "gpio25"; + function = "gpio"; + }; + + config { + pins = "gpio25"; + drive-strength = <2>; + bias-pull-up; + output-enable; + output-high; + }; + }; + fp_id0_init: fp_id0_init { + mux { + pins = "gpio39"; + function = "gpio"; + }; + + config { + pins = "gpio39"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + fp_id1_init: fp_id1_init { + mux { + pins = "gpio41"; + function = "gpio"; + }; + + config { + pins = "gpio41"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + fp_id2_init: fp_id2_init { + mux { + pins = "gpio63"; + function = "gpio"; + }; + + config { + pins = "gpio63"; + drive-strength = <2>; + bias-disable; + input-enable; + }; + }; + }; + + fingerprint_detect { + compatible = "oneplus,fpdetect"; + fp-gpio-enable = <&tlmm 25 0>; + fp-gpio-id0 = <&tlmm 39 0>; + fp-gpio-id1 = <&tlmm 41 0>; + fp-gpio-id2 = <&tlmm 63 0>; + pinctrl-names = "fp_enable", "fp_id_init"; + pinctrl-0 = <&fp_enable>; + pinctrl-1 = <&fp_id0_init &fp_id1_init &fp_id2_init>; + oem,dumpling; + }; + + qcom,bcl { + qcom,bcl-soc-hotplug-list = <&CPU6 &CPU7>; + qcom,ibat-monitor { + qcom,soc-low-threshold = <5>; + }; + }; + ramoops { + compatible = "ramoops"; + status = "ok"; + memory-region = <&ramoops_mem>; + }; + + /* Add 0x9c 0x21C, 0xf0 0x23C */ + qusb_phy0: qusb@c012000 { + qcom,qusb-phy-init-seq = + /* */ + <0x80 0x0 + 0x13 0x04 + 0x7c 0x18c + 0x80 0x2c + 0x0a 0x184 + 0x9c 0x21C + 0xf0 0x23C + 0x0f 0x240>; + }; + + force_enable_serial { + compatible = "oem,force_serial"; + }; + oem_serial_pinctrl { + compatible = "oem,oem_serial_pinctrl"; + pinctrl-names = "uart_pinctrl_active", + "uart_pinctrl_deactive"; + pinctrl-0 = <&uart_console_active>; + pinctrl-1 = <&uart_console_deactive>; + }; + bootloader_log { + compatible = "bootloader_log"; + linux,contiguous-region = <&bootloader_log_mem>; + }; +}; + +&pm8998_gpios { + /* GPIO 5 for Vol- Key */ + gpio@c400 { + status = "okay"; + qcom,mode = <0>; + qcom,pull = <0>; + qcom,vin-sel = <0>; + qcom,src-sel = <0>; + qcom,out-strength = <1>; + }; +}; + +&pm8998_rtc { + compatible = "qcom,qpnp-rtc"; + qcom,qpnp-rtc-alarm-pwrup = <1>; +}; + +&pmi8998_gpios { + /* button-backlight */ + gpio@c400 { + qcom,mode = <1>; /* Output */ + qcom,pull = <4>; /* Pull Down*/ + qcom,master-en = <1>; /* Enable GPIO */ + qcom,invert = <1>; + qcom,out-strength = <1>; + status = "okay"; + }; + +}; + +&sdhc_2 { + status = "disabled"; +}; + +&pmi8998_haptics { + status = "okay"; + qcom,wave-shape = "sine"; + qcom,play-mode = "buffer"; + qcom,wave-samples = [28 28 28 28 28 28 28 28]; + qcom,wave-samples-overdrive = [7e 7e 7e 7e 7e 28 28 28]; + qcom,brake-pattern = [03 03 03 03]; + qcom,wave-rep-cnt = <1>; + qcom,wave-samp-rep-cnt = <1>; + qcom,use-play-irq; + qcom,vmax-mv = <2088>; + qcom,wave-play-rate-us = <4255>; + qcom,lra-auto-res-mode = "zxd-eop"; + qcom,lra-res-cal-period = <32>; +}; + +&spmi_bus { + qcom,pmi8998@2 { + /* button-backlight */ + gpio-leds { + compatible = "gpio-leds"; + status = "okay"; + vdd-supply = <&pmi8998_bob>; + keypad-led-vbob-min = <3312000>; + keypad-led-vbob-max = <3600000>; + keypad-backlight { + //here maybe pm8998_gpios, depends on what you use. + gpios = <&pmi8998_gpios 5 0>; + label = "button-backlight"; + linux,default-trigger = "none"; + default-state = "off"; + }; + }; + }; + qcom,pm8998@0{ + qcom,power-on@800 { + qcom,pon-dbc-delay = <31250>; + qcom,kpdpwr-sw-debounce; + }; + }; + qcom,pmi8998@3 { + /* RGB LED blink */ + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&red_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <1>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + +&green_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <13>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + +&blue_led { + qcom,use-blink; + qcom,duty-pcts = [00 05 0a 0f 14 1d 28 32 3c 4b 64]; + qcom,duty-ms = <20>; + qcom,start-idx = <13>; + qcom,idx-len = <11>; + qcom,lut-flags = <0x1f>; + qcom,ramp-step-ms = <100>; + qcom,pause-lo = <2000>; + qcom,pause-hi = <1000>; + /delete-property/ linux,default-trigger; +}; + + + + +/* Charging */ +&soc { + tlmm: pinctrl@03400000 { + oneplus_fastchg { + usb_sw_active: usb_sw_active { + mux { + pins = "gpio90", "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio90", "gpio91"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + usb_sw_suspend: usb_sw_suspend { + mux { + pins = "gpio90", "gpio91"; + function = "gpio"; + }; + + config { + pins = "gpio90", "gpio91"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; +}; + +&i2c_7 { + bq27541-battery@55 { + status = "ok"; + compatible = "ti,bq27541-battery"; + reg = <0x55>; + qcom,modify-soc-smooth; + }; + oneplus_fastcg@26{ + status = "ok"; + compatible = "microchip,oneplus_fastcg"; + reg = <0x26>; + microchip,mcu-en-gpio = <&tlmm 96 0x00>; + microchip,usb-sw-1-gpio = <&tlmm 90 0x00>; + microchip,usb-sw-2-gpio = <&tlmm 91 0x00>; + microchip,ap-clk = <&tlmm 79 0x00>; + microchip,ap-data = <&tlmm 80 0x00>; + pinctrl-names = "mux_fastchg_active", + "mux_fastchg_suspend", + "mcu_data_active", + "mcu_data_suspend"; + pinctrl-0 = <&fastchg_active + &usb_sw_active + &ap_clk_active >; + pinctrl-1 = <&usb_sw_suspend + &fastchg_suspend + &ap_clk_suspend>; + pinctrl-2 =<&ap_data_active>; + pinctrl-3 =<&ap_data_suspend>; + clocks = <&clock_gcc clk_uart_snoc_msmbus_a_clk>, + <&clock_gcc clk_uart_cnoc_msmbus_a_clk>; + clock-names = "snoc", "cnoc"; + }; +}; +&soc { + tlmm: pinctrl@03400000 { + oneplus_fastchg { + fastchg_active: fastchg_active { + mux { + pins = "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio96"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + fastchg_suspend: fastchg_suspend { + mux { + pins = "gpio96"; + function = "gpio"; + }; + + config { + pins = "gpio96"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_clk_active: ap_clk_active { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_clk_suspend: ap_clk_suspend { + mux { + pins = "gpio79"; + function = "gpio"; + }; + + config { + pins = "gpio79"; + drive-strength = <2>; + bias-disable; + }; + }; + + ap_data_active: ap_data_active { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <16>; + bias-pull-up; + }; + }; + + ap_data_suspend: ap_data_suspend { + mux { + pins = "gpio80"; + function = "gpio"; + }; + + config { + pins = "gpio80"; + drive-strength = <2>; + bias-disable; + }; + }; + }; + }; +}; + +&pmi8998_charger { + /*Set FCC MAX 500mA by default,FCC will*/ + /* be set again after check battery temperature*/ + qcom,fcc-max-ua = <500000>; + qcom,usb-icl-ua = <1800000>; + qcom,fv-max-uv = <4365000>; + /* ibatmax setting for different temp regions */ + ibatmax-little-cold-ma = <325>; + ibatmax-cool-ma = <450>; + ibatmax-little-cool-ma = <775>; + ibatmax-pre-normal-ma = <1425>; + ibatmax-normal-ma = <1950>; + ibatmax-warm-ma = <750>; + /* vbatmax setting for different temp regions */ + vbatmax-little-cold-mv = <3975>; //3980 + vbatmax-cool-mv = <4380>; + vbatmax-little-cool-mv = <4380>; + vbatmax-pre-normal-mv = <4380>; + vbatmax-normal-mv = <4380>; + vbatmax-warm-mv = <4080>; + /* vbatdet setting for different temp regions */ + vbatdet-little-cold-mv = <3700>; + vbatdet-cool-mv = <4150>; + vbatdet-little-cool-mv = <4270>; + vbatdet-pre-normal-mv = <4270>; + vbatdet-normal-mv = <4270>; + vbatdet-warm-mv = <3980>; + /* temp region settings */ + cold-bat-decidegc = <30>; + little-cold-bat-decidegc = <0>; + cool-bat-decidegc = <50>; + little-cool-bat-decidegc = <120>; + pre-normal-bat-decidegc = <160>; + warm-bat-decidegc = <450>; + hot-bat-decidegc = <530>; + /* other settings */ + qcom,cutoff-voltage-with-charger = <3250>; + disable-pd; +}; + +&pmi8998_fg { + oem,use_external_fg; + qcom,fg-rsense-sel = <0>; +}; + +&i2c_5 { + synaptics@20 { + status = "disabled"; + }; + synaptics-rmi-ts@20 { + compatible = "synaptics,s3320"; + reg = <0x20>; + interrupt-parent = <&tlmm>; + interrupts = <125 0x2008>; + vcc_i2c_1v8-supply = <&pm8998_l6>; + vdd_2v8-supply = <&pm8998_l28>; + synaptics,tx-rx-num = <15 30>; + synaptics,vdd-voltage = <1808000 1808000>; + synaptics,avdd-voltage = <3008000 3008000>; + synaptics,vdd-current = <40000>; + synaptics,avdd-current = <20000>; + synaptics,display-coords = <1080 2160>; + synaptics,panel-coords = <1080 2160>; + synaptics,reset-gpio = <&tlmm 89 0x00>; + synaptics,irq-gpio = <&tlmm 125 0x2008>; + oem,support_1080x2160_tp; + oem,support_hw_poweroff; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + }; +}; + +&pcie0_clkreq_default { + mux { + pins = "gpio36"; + function = "gpio"; + }; + + config { + pins = "gpio36"; + drive-strength = <2>; + bias-pull-up; + }; +}; + + +&mdss_dp_ctrl { + status = "disabled"; +}; + +&mdss_dp_pll { + status = "disabled"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi1 { + status = "disabled"; +}; + +&labibb { + status = "disabled"; +}; + +&mdss_hdmi_tx { + status = "disabled"; +}; + +&mdss_hdmi_pll { + status = "disabled"; +}; + +&snd_9335 { + qcom,mbhc-audio-jack-type = "4-pole-jack"; +}; +/* + * liuhaituo@MultiMediaAudio modified for adapation phone + * headset and AKG headset-mic 2018/4/26 + */ +&slim_aud { + tasha_codec { + qcom,cdc-micbias1-mv = <1800>; + qcom,cdc-micbias2-mv = <2700>; + qcom,cdc-micbias3-mv = <1800>; + qcom,cdc-micbias4-mv = <1800>; + }; +}; + +/* Audio */ +&soc { + i2c@c1b7000 { + status = "ok"; + test_tfa9891: tfa9891@36 { + compatible = "nxp,tfa9891"; + reg = <0x36>; + reset-gpio= <&tlmm 49 0>; + irq-gpio= <&tlmm 50 0>; + bob_power-supply = <&pmi8998_bob>; + qcom,bob_power-voltage-level = <2950000 2960000>; + qcom,bob_power-current-level = <200 800000>; + status = "ok"; + }; + }; + + sound-9335 { + op,project_17801; + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "AMIC3", "MIC BIAS3", + "MIC BIAS3", "Analog Mic3", + "AMIC4", "MIC BIAS1", + "MIC BIAS1", "Analog Mic4", + "AMIC5", "MIC BIAS4", + "MIC BIAS4", "Analog Mic5", + "AMIC6", "MIC BIAS4", + "MIC BIAS4", "Analog Mic6", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC1", "MIC BIAS1", + "MIC BIAS1", "Digital Mic1", + "DMIC2", "MIC BIAS4", + "MIC BIAS4", "Digital Mic2", + "DMIC3", "MIC BIAS4", + "MIC BIAS4", "Digital Mic3", + "DMIC4", "MIC BIAS3", + "MIC BIAS3", "Digital Mic4", + "DMIC5", "MIC BIAS3", + "MIC BIAS3", "Digital Mic5", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; + qcom,us-euro-gpios = <>; + qcom,msm-mbhc-hphl-swh = <1>; + qcom,msm-mbhc-gnd-swh = <1>; + qcom,wsa-max-devs = <>; + qcom,wsa-devs = <>; + qcom,wsa-aux-dev-prefix = <>; + }; + + sound-tavil { + qcom,usbc-analog-en1_gpio = <>; + qcom,usbc-analog-en2_n_gpio = <>; + }; +}; + +&i2c_9 { /* BLSP2 QUP9 for Audio Speaker */ + qcom,master-id = <86>; + qcom,clk-freq-out = <400000>; + status = "ok"; +}; + +&dai_mi2s3 { + qcom,msm-mi2s-rx-lines = <2>; + qcom,msm-mi2s-tx-lines = <1>; + qcom,msm-mi2s-slave = <0>; + qcom,msm-mi2s-ext-mclk = <0>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&quat_mi2s_active + &quat_mi2s_sd0_active + &quat_mi2s_sd1_active>; + pinctrl-1 = <&quat_mi2s_sleep + &quat_mi2s_sd0_sleep + &quat_mi2s_sd1_sleep>; +}; + +&wcd_us_euro_gpio { + status = "disabled"; +}; + +&wcd_usbc_analog_en1_gpio { + status = "disabled"; +}; + +&wcd_usbc_analog_en2n_gpio { + status = "disabled"; +}; + +&clock_audio_lnbb { + status = "disabled"; +}; + + +/* Camera */ +&cci { + qcom,eeprom@1 { + status = "disabled"; + }; + + qcom,camera@1 { + status = "disabled"; + }; + + qcom,eeprom@2 { + status = "ok"; + }; + +}; + +&firmware { + android { + compatible = "android,firmware"; + fstab { + compatible = "android,fstab"; + vendor { + compatible = "android,vendor"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + }; + }; +}; + +/* External Thermistors */ +&pm8998_vadc { + chan@4e { + label = "ufs_therm"; + reg = <0x4e>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@4f { + label = "pa_therm0"; + reg = <0x4f>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@50 { + label = "pa_therm1"; + reg = <0x50>; + qcom,decimation = <2>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; +}; + +&pm8998_adc_tm { + chan@4e { + label = "ufs_therm"; + reg = <0x4e>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x80>; + qcom,thermal-node; + }; + + chan@4f { + label = "pa_therm0"; + reg = <0x4f>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x88>; + qcom,thermal-node; + }; + + chan@50 { + label = "pa_therm1"; + reg = <0x50>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; + qcom,hw-settle-time = <2>; + qcom,btm-channel-number = <0x90>; + qcom,thermal-node; + }; +}; + +&rpm_bus { + rpm-regulator-ldoa25 { + pm8998_l25: regulator-l25 { + regulator-min-microvolt = <3312000>; + }; + + pm8998_l25_pin_ctrl: regulator-l25-pin-ctrl { + regulator-min-microvolt = <3312000>; + }; + }; +}; + + +/*Anderson-Config_UARTPIN_as_GPIO*[*/ +&soc { + tlmm: pinctrl@03400000 { + uart_console_deactive: uart_console_deactive { + mux { + pins = "gpio4", "gpio5"; + function = "gpio"; + }; + + config { + pins = "gpio4", "gpio5"; + drive-strength = <2>; + bias-pull-down; + }; + }; + }; +}; + +&uartblsp2dm1 { + pinctrl-names = "uart_active", "uart_deactive"; + pinctrl-0 = <&uart_console_active>; + pinctrl-1 = <&uart_console_deactive>; +}; +/*Anderson-Config_UARTPIN_as_GPIO*]**/ + +/*pcie 0 don't used,gpio 37 will used in nfc.*/ +/*but gpio37 will request in pcie0. so disable it*/ +&pcie0 { + status = "disabled"; +}; + +&pm8998_gpios { + gpio@d400 { + qcom,pull = <4>; + }; +}; + +&spi_1 { + status = "ok"; + qcom,disable-autosuspend; + + ese@0 { + compatible = "nxp,p61"; + reg = <0>; + spi-max-frequency = <8000000>; + }; +}; + +&i2c_6 { + nq@28 { + status = "disabled"; + }; + + pn5xx@28 { + compatible = "nxp,pn544"; + reg = <0x28>; + nxp,pn544-irq = <&tlmm 92 0x00>; + nxp,pn544-ven = <&tlmm 12 0x00>; + nxp,pn544-fw-dwnld = <&tlmm 93 0x00>; + nxp,pn544-clk-gpio = <&pm8998_gpios 21 0x00>; + nxp,pn544-ese-pwr = <&tlmm 37 0x00>; + nfc_voltage_s4-supply = <&pm8998_s4>; + nxp,pn544-wake-up = <&tlmm 116 0x00>; + interrupt-parent = <&tlmm>; + qcom,clk-src = "BBCLK3"; + interrupts = <92 0>; + interrupt-names = "nfc_irq"; + pinctrl-names = "nfc_active", "nfc_suspend"; + pinctrl-0 = <&nfc_int_active &nfc_enable_active>; + pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>; + clocks = <&clock_gcc clk_ln_bb_clk3_pin>; + clock-names = "ref_clk"; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi b/arch/arm/boot/dts/qcom/msm-pm8998.dtsi index e13cdf4c28e7..617cf0408a49 100644 --- a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm8998.dtsi @@ -43,6 +43,10 @@ qcom,pon-type = <0>; qcom,pull-up = <1>; linux,code = <116>; + qcom,support-reset = <1>; + qcom,s1-timer = <10256>; + qcom,s2-timer = <2000>; + qcom,s2-type = <7>; }; qcom,pon_2 { diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi index 4c049c8007cc..0cc5cea4dcfc 100644 --- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi @@ -342,7 +342,8 @@ qcom,fg-esr-timer-charging = <0 96>; qcom,cycle-counter-en; status = "okay"; - + qcom,fg-sys-term-current = <190>; + qcom,fg-chg-term-current = <180>; qcom,fg-batt-soc@4000 { status = "okay"; reg = <0x4000 0x100>; diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi index 86b68b2440a9..15071f2e5bc7 100644 --- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi @@ -347,17 +347,17 @@ &cam_sensor_front_suspend &led_disable>; gpios = <&tlmm 14 0>, <&tlmm 28 0>, - <&pm8998_gpios 9 0>, - <&tlmm 21 0>; + <&pm8998_gpios 9 0>/* delete by xcb, gpio 21 are used by lcd, + <&tlmm 21 0>*/; qcom,gpio-reset = <1>; qcom,gpio-vdig = <2>; - qcom,gpio-flash-en = <3>; - qcom,gpio-req-tbl-num = <0 1 2 3>; - qcom,gpio-req-tbl-flags = <1 0 0 0>; + /*qcom,gpio-flash-en = <3>;*/ + qcom,gpio-req-tbl-num = <0 1 2 /*3*/>; + qcom,gpio-req-tbl-flags = <1 0 0 /*0*/>; qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", - "CAM_VDIG", - "FLASH_EN"; + "CAM_VDIG"/*, + "FLASH_EN"*/; qcom,sensor-position = <1>; qcom,sensor-mode = <0>; qcom,cci-master = <1>; diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi index ddc367711dab..32826e87e92b 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi @@ -11,7 +11,7 @@ */ #include -#include "msm8998-camera-sensor-mtp.dtsi" +//#include "msm8998-camera-sensor-mtp.dtsi" &vendor { bluetooth: bt_wcn3990 { compatible = "qca,wcn3990"; @@ -150,16 +150,6 @@ status = "okay"; }; - gpio@d200 { /* GPIO 19 - wil6210 refclk3_en */ - qcom,mode = <0>; /* Input */ - qcom,pull = <5>; /* No Pull */ - qcom,vin-sel = <1>; /* VIN1 GPIO_MV */ - qcom,src-sel = <0>; /* GPIO */ - qcom,invert = <0>; /* Invert */ - qcom,master-en = <1>; /* Enable GPIO */ - status = "okay"; - }; - /* GPIO 21 (NFC_CLK_REQ) */ gpio@d400 { qcom,mode = <0>; @@ -614,6 +604,7 @@ &vendor { mtp_batterydata: qcom,battery-data { qcom,batt-id-range-pct = <15>; + #include "OP-batterydata-3300mah.dtsi" #include "fg-gen3-batterydata-itech-3000mah.dtsi" #include "fg-gen3-batterydata-ascent-3450mah.dtsi" #include "fg-gen3-batterydata-demo-6000mah.dtsi" diff --git a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi index abc0247b4475..575b73cc045e 100644 --- a/arch/arm/boot/dts/qcom/msm8998-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-v2.dtsi @@ -1121,10 +1121,10 @@ <0x01fcb24c 0x4>, <0x00784238 0x4>; reg-names = "qusb_phy_base", - "tcsr_clamp_dig_n_1p8", - "efuse_addr"; - qcom,efuse-bit-pos = <16>; - qcom,efuse-num-bits = <4>; + "tcsr_clamp_dig_n_1p8"; + // "efuse_addr"; +// qcom,efuse-bit-pos = <16>; +// qcom,efuse-num-bits = <4>; qcom,qusb-phy-init-seq = /* */ <0x13 0x04 /* analog_controls_two */ diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 6990099978c6..b4921f50b71e 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -292,7 +292,15 @@ dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor"; type = "ext4"; mnt_flags = "ro,barrier=1,discard"; - fsmgr_flags = "wait,slotselect,verify"; + fsmgr_flags = "wait,verify"; + status = "ok"; + }; + system { + compatible = "android,system"; + dev = "/dev/block/platform/soc/1da4000.ufshc/by-name/system"; + type = "ext4"; + mnt_flags = "ro,barrier=1,discard"; + fsmgr_flags = "wait,verify"; status = "ok"; }; }; diff --git a/arch/arm/boot/dts/qcom/pxlw-iris-samsung_s6e3fa3.dtsi b/arch/arm/boot/dts/qcom/pxlw-iris-samsung_s6e3fa3.dtsi new file mode 100644 index 000000000000..76481ce9c1b6 --- /dev/null +++ b/arch/arm/boot/dts/qcom/pxlw-iris-samsung_s6e3fa3.dtsi @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------- + * Iris2p Setting for Samsung S6e3fa3 Panel + *---------------------------------------------------------------------------*/ + /* iris mipi mode */ + qcom,iris-mipitx-type = "dsi_cmd_mode"; /* dsi_video_mode/dsi_cmd_mode */ + qcom,iris-mipirx-channel = <0x1>; /*dual channel*/ + qcom,iris-mipitx-channel = <0x1>; + qcom,iris-mipirx-dsc = <0x0>; /*non DSC*/ + qcom,iris-mipitx-dsc = <0x0>; + qcom,iris-mipirx-pxl-mode = <0x0>; + qcom,iris-mipitx-pxl-mode = <0x1>; + + qcom,mdss-dsi-on-command-to-video-panel = [ + 05 01 00 00 32 00 02 11 00 + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 02 B9 01 + 29 01 00 00 00 00 03 F0 A5 A5 + 29 01 00 00 00 00 03 F0 5A 5A + 29 01 00 00 00 00 03 FC 5A 5A + 29 01 00 00 00 00 03 F4 00 01 + 29 01 00 00 00 00 02 C0 32 + 29 01 00 00 00 00 02 F7 03 + 29 01 00 00 00 00 03 F0 A5 A5 + 29 01 00 00 00 00 03 FC A5 A5 + 29 01 00 00 00 00 02 53 20 + 29 01 00 00 00 00 02 57 40 + 29 01 00 00 00 00 02 55 00 + //05 01 00 00 10 00 02 29 00 + ]; + qcom,mdss-dsi-on-command-to-video-panel-state = "dsi_lp_mode"; + + qcom,mdss-dsi-off-command-to-video-panel = [ + 39 01 00 00 32 00 02 28 00 + 39 01 00 00 78 00 02 10 00 + ]; + qcom,mdss-dsi-off-command-to-video-panel-state = "dsi_hs_mode"; + + qcom,mdss-dsi-on-command-to-iris-mipirx = [ + 15 01 00 00 01 00 02 3a 77 /* rgb_888 */ + 15 01 00 00 01 00 02 35 00 /* set tearing on */ + 39 01 00 00 01 00 05 2a 00 00 04 37 + 39 01 00 00 01 00 05 2b 00 00 07 7f + ]; + qcom,mdss-dsi-on-command-to-iris-mipirx-state = "dsi_hs_mode"; + /* iris sys */ + qcom,iris-pll-setting = < + 0x0002 0x430901 0x655555 /*PPLL_CTRL0 PPLL_CTRL1 PPLL_CTRL2*/ + 0x2002 0x201101 0x41D97F /*DPLL_CTRL0 DPLL_CTRL1 DPLL_CTRL2*/ + 0x003f 0x3E0901 0x800000 /*MPLL_CTRL0 MPLL_CTRL1 MPLL_CTRL2*/ + 0x0000 0x000002 0x000000 /*TXPLL_DIV TXPLL_SEL reserved*/ + >; + qcom,iris-clock-setting = [01 01 09 12 09 72]; /* Dclk Inclk Mcuclk Pclk Mclk Escclk */ + + iris,analog-bypass-mode-enabled; + + /* iris mipi rx */ + qcom,mipirx-dsi-functional-program = <0x64>; + qcom,mipirx-data-lane-timing-param = <0xFF08>; + qcom,mipirx-eot-ecc-crc-disable = <0x7>; + + /* iris2p mipitx */ + qcom,mipitx-dsi-tx-ctrl = <0xA00C139>; + qcom,mipitx-hs-tx-timer = <0x762125>; + qcom,mipitx-bta-lp-timer = <0xFFFF17>; + qcom,mipitx-initialization-reset-timer = <0xA8C078F>; + qcom,mipitx-dphy-timing-margin = <0x40401>; + qcom,mipitx-lp-timing-para = <0xE010006>; + qcom,mipitx-data-lane-timing-param1 = <0x120B1005>; + qcom,mipitx-clock-lane-timing-param = <0x120A2805>; + qcom,mipitx-dphy-pll-para = <0x78F>; + qcom,mipitx-dphy-trim-1 = <0xEDB5380C>; + + + /*iris out timing*/ + qcom,iris-out-panel-width = <1080>; + qcom,iris-out-panel-height = <1920>; + qcom,iris-out-h-pulse-width = <19>; + qcom,iris-out-h-back-porch = <70>; + qcom,iris-out-h-front-porch = <120>; + qcom,iris-out-v-pulse-width = <2>; + qcom,iris-out-v-back-porch = <4>; + qcom,iris-out-v-front-porch = <76>; + + qcom,iris-delta-period-min = <50>; + qcom,iris-delta-period-max = <58>; + qcom,iris-reset-sequence = <0 3>,<1 2>; + qcom,iris-enabled; \ No newline at end of file diff --git a/arch/arm/boot/dts/qcom/pxlw-iris-settings.dtsi b/arch/arm/boot/dts/qcom/pxlw-iris-settings.dtsi new file mode 100644 index 000000000000..c32c7249d128 --- /dev/null +++ b/arch/arm/boot/dts/qcom/pxlw-iris-settings.dtsi @@ -0,0 +1,110 @@ +/* Copyright (c) 2013, 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. + */ + +/*--------------------------------------------------------------------------- + * This file is autogenerated file using gcdb parser. Please do not edit it. + * Update input XML file to add a new entry or update variable in this file + * VERSION = "1.0" + *---------------------------------------------------------------------------*/ +&mdss_mdp { + pxlw_iris_settings: pxlw,mdss_iris_settings { + /*iris last frame repeat cnt*/ + pxlw,iris-last-frame-repeat-cnt = <0x00>; + + /*disable dbc settings*/ + pxlw,iris-disable-dbc-dlv-sensitivity = <0x00>; + pxlw,iris-disable-dbc-quality = <0x00>; + + /*disable pq settings*/ + pxlw,iris-disable-pq-peaking = <0x00>; + pxlw,iris-disable-pq-peaking-demo= <0x00>; + pxlw,iris-disable-pq-gamma = <0x00>; + pxlw,iris-disable-pq-contrast = <0x32>; + + /*disable lce setting*/ + pxlw,iris-disable-lce = <0x00>; + + /*disable cm*/ + pxlw,iris-disable-cm-c6axes = <0x00>; + pxlw,iris-disable-cm-c3d = <0x00>; + pxlw,iris-disable-cm-fleshtone = <0x00>; + pxlw,iris-disable-color-temp-en = <0x00>; + pxlw,iris-disable-reading-mode = <0x00>; + /*disable color adjust*/ + pxlw,iris-disable-color-adjust = <0x32>; + + /*use analog bypass or bypass*/ + pxlw,iris-is-analog-bypass = <0x1>; + + /*default setting for APPCODE init data */ + pxlw,iris-pq-peaking-default = <0x00>; + pxlw,iris-pq-sharpness-default = <0x00>; + pxlw,iris-pq-memc-demo-mode-default = <0x00>; + pxlw,iris-pq-peaking-demo-mode-default = <0x00>; + pxlw,iris-pq-gamma-mode-default = <0x00>; + pxlw,iris-pq-memclevel-default = <0x03>; + pxlw,iris-pq-contrast-default = <0x32>; + pxlw,iris-pq-cinema-default = <0x00>; + + pxlw,iris-cabcmode-default = <0x01>; + + pxlw,iris-color-adjust-default = <0x32>; + + pxlw,iris-lce-mode-default = <0x00>; + pxlw,iris-lce-mode1-level-default = <0x01>; + pxlw,iris-lce-mode2-level-default = <0x01>; + pxlw,iris-lce-demo-mode-default = <0x00>; + pxlw,iris-lux-value-default = <0x00>; + pxlw,iris-cct-value-default = <0x00>; + + pxlw,iris-reading-mode-default = <0x00>; + + pxlw,iris-cm-6axes-default = <0x00>; + pxlw,iris-cm-3d-default = <0x00>; + pxlw,iris-cm-demo-mode-default = <0x00>; + pxlw,iris-cm-fleshtone-default = <0x00>; + pxlw,iris-color-temp-en-default = <0x00>; + pxlw,iris-color-temp-default = <0x00>; + pxlw,iris-sensor-auto-en-default = <0x00>; + + /* setting for AP init data */ + pxlw,iris-pq-peaking-init = <0x00>; + pxlw,iris-pq-sharpness-init = <0x00>; + pxlw,iris-pq-memc-demo-mode-init = <0x00>; + pxlw,iris-pq-peaking-demo-mode-init = <0x00>; + pxlw,iris-pq-gamma-mode-init = <0x00>; + pxlw,iris-pq-memclevel-init = <0x03>; + pxlw,iris-pq-contrast-init = <0x32>; + pxlw,iris-pq-cinema-init = <0x00>; + + pxlw,iris-cabcmode-init = <0x00>; + + pxlw,iris-color-adjust-init = <0x32>; + + pxlw,iris-lce-mode-init = <0x00>; + pxlw,iris-lce-mode1-level-init = <0x01>; + pxlw,iris-lce-mode2-level-init = <0x01>; + pxlw,iris-lce-demo-mode-init = <0x00>; + + pxlw,iris-lux-value-init = <0x00>; + pxlw,iris-cct-value-init = <0x00>; + pxlw,iris-reading-mode-init = <0x00>; + pxlw,iris-cm-6axes-init = <0x00>; + pxlw,iris-cm-3d-init = <0x00>; + pxlw,iris-cm-demo-mode-init = <0x00>; + pxlw,iris-cm-fleshtone-init = <0x00>; + pxlw,iris-color-temp-en-init = <0x00>; + pxlw,iris-color-temp-init = <0x00>; + pxlw,iris-sensor-auto-en-init = <0x00>; + }; +}; + diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 6441404ee2ed..18f2a877d46a 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -1,18 +1,18 @@ -dts-dirs += altera -dts-dirs += amd -dts-dirs += apm -dts-dirs += arm -dts-dirs += broadcom -dts-dirs += cavium -dts-dirs += exynos -dts-dirs += freescale -dts-dirs += hisilicon -dts-dirs += marvell -dts-dirs += mediatek +#dts-dirs += altera +#dts-dirs += amd +#dts-dirs += apm +#dts-dirs += arm +#dts-dirs += broadcom +#dts-dirs += cavium +#dts-dirs += exynos +#dts-dirs += freescale +#dts-dirs += hisilicon +#dts-dirs += marvell +#dts-dirs += mediatek dts-dirs += qcom -dts-dirs += rockchip -dts-dirs += sprd -dts-dirs += xilinx +#dts-dirs += rockchip +#dts-dirs += sprd +#dts-dirs += xilinx subdir-y := $(dts-dirs) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 365e13004651..91d3fad5b602 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -229,3 +229,5 @@ CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_HIBERNATION=y CONFIG_KPROBES=y CONFIG_CORESIGHT=y + +CONFIG_TRI_STATE_KEY=y diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig index 3ea24a4bdfb0..ed15fdad9a52 100644 --- a/arch/arm64/configs/msm-perf_defconfig +++ b/arch/arm64/configs/msm-perf_defconfig @@ -611,3 +611,4 @@ CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_QMI_ENCDEC=y +CONFIG_TRI_STATE_KEY=y diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig index e0b9dd5f0a6f..61e2549e05ae 100644 --- a/arch/arm64/configs/msm_defconfig +++ b/arch/arm64/configs/msm_defconfig @@ -596,6 +596,7 @@ CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_MEMORY_INIT=y CONFIG_LOCKUP_DETECTOR=y @@ -649,3 +650,4 @@ CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_XZ_DEC=y CONFIG_QMI_ENCDEC=y +CONFIG_TRI_STATE_KEY=y diff --git a/arch/arm64/configs/msmcortex-oqc_defconfig b/arch/arm64/configs/msmcortex-oqc_defconfig new file mode 100644 index 000000000000..c5c522b0d5d9 --- /dev/null +++ b/arch/arm64/configs/msmcortex-oqc_defconfig @@ -0,0 +1,788 @@ +CONFIG_LOCALVERSION="-aging" +# CONFIG_USELIB is not set +CONFIG_AUDIT=y +# CONFIG_AUDITSYSCALL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IRQ_TIME_ACCOUNTING=y +CONFIG_TASKSTATS=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_RCU_NOCB_CPU_ALL=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_CPU_MAX_BUF_SHIFT=17 +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHEDTUNE=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_HMP=y +CONFIG_SCHED_HMP_CSTATE_AWARE=y +CONFIG_SCHED_CORE_CTL=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_SCHED_AUTOGROUP=y +CONFIG_SCHED_TUNE=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +CONFIG_KALLSYMS_ALL=y +# CONFIG_MEMBARRIER is not set +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE=y +# CONFIG_MODULE_SIG_SHA512=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_QCOM=y +CONFIG_ARCH_MSM8998=y +CONFIG_ARCH_MSMHAMSTER=y +CONFIG_PCI=y +CONFIG_PCI_MSM=y +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_QCOM_TLB_EL2_HANDLER=y +CONFIG_PREEMPT=y +CONFIG_HZ_100=y +CONFIG_CLEANCACHE=y +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y +CONFIG_ZSMALLOC=y +CONFIG_BALANCE_ANON_FILE_RECLAIM=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_SW_TTBR0_PAN=y +CONFIG_RANDOMIZE_BASE=y +CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_BOOST=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_NET_IPVTI=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_INET_UDP_DIAG=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_IPTABLES_128=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_L2TP_DEBUGFS=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_FLOW=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=y +CONFIG_NET_EMATCH_NBYTE=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=y +CONFIG_NET_EMATCH_TEXT=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_NET_ACT_SKBEDIT=y +CONFIG_DNS_RESOLVER=y +CONFIG_RMNET_DATA=y +CONFIG_RMNET_DATA_FC=y +CONFIG_RMNET_DATA_DEBUG_PKT=y +CONFIG_SOCKEV_NLMCAST=y +CONFIG_BT=y +CONFIG_MSM_BT_POWER=y +CONFIG_BTFM_SLIM=y +CONFIG_BTFM_SLIM_WCN3990=y +CONFIG_CFG80211=y +CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CFG80211_REG_CELLULAR_HINTS=y +CONFIG_CFG80211_INTERNAL_REGDB=y +# CONFIG_CFG80211_CRDA_SUPPORT is not set +CONFIG_RFKILL=y +# CONFIG_NFC_NQ=y +CONFIG_NFC_PN5XX=y +CONFIG_NFC_PN80T=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_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_QSEECOM=y +CONFIG_HDCP_QSEECOM=y +CONFIG_UID_SYS_STATS=y +CONFIG_QPNP_MISC=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_QCOM=y +CONFIG_SCSI_UFS_QCOM_ICE=y +CONFIG_SCSI_UFSHCD_CMD_LOGGING=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_REQ_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_ANDROID_VERITY=y +CONFIG_NETDEVICES=y +CONFIG_BONDING=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_MSM_RMNET_MHI=y +CONFIG_RNDIS_IPA=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_USB_USBNET=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_ATH_CARDS=y +CONFIG_WIL6210=m +CONFIG_CLD_LL_CORE=y +CONFIG_CNSS_GENL=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI=y +CONFIG_INPUT_FINGERPRINT=y +CONFIG_FINGERPRINT_DETECT=y +CONFIG_FINGERPRINT_FPC=y +CONFIG_FINGERPRINT_GOODIX=y +CONFIG_SECURE_TOUCH=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_HBTP_INPUT=y +CONFIG_INPUT_QPNP_POWER_ON=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_MSM_SMD=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM_LEGACY=y +# CONFIG_DEVPORT is not set +CONFIG_MSM_ADSPRPC=y +CONFIG_MSM_RDBG=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MSM_V2=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_SOUNDWIRE=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=y +CONFIG_SPMI=y +CONFIG_PINCTRL_MSM8998=y +CONFIG_PINCTRL_SDM660=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_RESET_QCOM=y +CONFIG_QCOM_DLOAD_MODE=y +CONFIG_POWER_RESET_XGENE=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_QPNP_FG_GEN3=y +CONFIG_FG_BQ27541=y +CONFIG_ONEPLUS_FASTCHG=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y +CONFIG_MSM_BCL_CTL=y +CONFIG_MSM_BCL_PERIPHERAL_CTL=y +CONFIG_BATTERY_BCL=y +CONFIG_QPNP_SMB2=y +CONFIG_SMB138X_CHARGER=y +CONFIG_QPNP_QNOVO=y +CONFIG_MSM_PM=y +CONFIG_APSS_CORE_EA=y +CONFIG_MSM_APM=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_CPU_THERMAL=y +CONFIG_LIMITS_MONITOR=y +CONFIG_LIMITS_LITE_HW=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_QPNP=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_QCOM_THERMAL_LIMITS_DCVS=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_I2C_PMIC=y +CONFIG_WCD9335_CODEC=y +CONFIG_WCD934X_CODEC=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_RPM_SMD=y +CONFIG_REGULATOR_QPNP=y +CONFIG_REGULATOR_QPNP_LABIBB=y +CONFIG_REGULATOR_QPNP_LCDB=y +CONFIG_REGULATOR_SPM=y +CONFIG_REGULATOR_CPR3_HMSS=y +CONFIG_REGULATOR_CPR3_MMSS=y +CONFIG_REGULATOR_CPRH_KBSS=y +CONFIG_REGULATOR_MEM_ACC=y +CONFIG_REGULATOR_PROXY_CONSUMER=y +CONFIG_REGULATOR_STUB=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_MSM_CAMERA=y +CONFIG_MSM_CAMERA_DEBUG=y +CONFIG_MSMB_CAMERA=y +CONFIG_MSMB_CAMERA_DEBUG=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CPP=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI20_HEADER=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSI30_HEADER=y +CONFIG_MSM_CSI31_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_IMX134=y +CONFIG_IMX132=y +CONFIG_OV9724=y +CONFIG_OV5648=y +CONFIG_GC0339=y +CONFIG_OV8825=y +CONFIG_OV8865=y +CONFIG_s5k4e1=y +CONFIG_OV12830=y +CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y +CONFIG_MSMB_JPEG=y +CONFIG_MSM_FD=y +CONFIG_MSM_JPEGDMA=y +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 +CONFIG_DVB_MPQ_DEMUX=m +CONFIG_TSPP=m +CONFIG_QCOM_KGSL=y +CONFIG_FB=y +CONFIG_FB_VIRTUAL=y +CONFIG_FB_MSM=y +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_HDMI_PANEL=y +CONFIG_FB_MSM_MDSS_DP_PANEL=y +CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_USB_AUDIO_QMI=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8998=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_ELECOM=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_PLANTRONICS=y +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_ISP1760=y +CONFIG_USB_ISP1760_HOST_ROLE=y +CONFIG_USB_PD_POLICY=y +CONFIG_QPNP_USB_PDPHY=y +CONFIG_USB_EHSET_TEST_FIXTURE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MSM_SSPHY_QMP=y +CONFIG_MSM_QUSB_PHY=y +CONFIG_DUAL_ROLE_USB_INTF=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_MTP=y +CONFIG_USB_CONFIGFS_F_PTP=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_DIAG=y +CONFIG_USB_CONFIGFS_F_GSI=y +CONFIG_USB_CONFIGFS_F_CDEV=y +CONFIG_USB_CONFIGFS_F_QDSS=y +CONFIG_USB_CONFIGFS_F_CCID=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_RING_BUFFER=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_QPNP_FLASH_V2=y +CONFIG_LEDS_QPNP_WLED=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_SWITCH=y +CONFIG_EDAC=y +CONFIG_EDAC_MM_EDAC=y +CONFIG_EDAC_CORTEX_ARM64=y +CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y +CONFIG_PAGE_POISONING=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_QPNP=y +CONFIG_ESOC=y +CONFIG_ESOC_DEV=y +CONFIG_ESOC_CLIENT=y +# CONFIG_ESOC_DEBUG is not set +CONFIG_ESOC_MDM_4x=y +CONFIG_ESOC_MDM_DRV=y +CONFIG_ESOC_MDM_DBG_ENG=y +CONFIG_DMADEVICES=y +CONFIG_QCOM_SPS_DMA=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_QPNP_REVID=y +CONFIG_QPNP_COINCELL=y +CONFIG_SPS=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_IPA=y +CONFIG_RMNET_IPA=y +CONFIG_GSI=y +CONFIG_IPA3=y +CONFIG_RMNET_IPA3=y +CONFIG_GPIO_USB_DETECT=y +CONFIG_MSM_MHI=y +CONFIG_MSM_MHI_UCI=y +# CONFIG_MSM_MHI_DEBUG is not set +CONFIG_SEEMP_CORE=y +CONFIG_USB_BAM=y +CONFIG_MSM_MDSS_PLL=y +CONFIG_REMOTE_SPINLOCK_MSM=y +CONFIG_MSM_TIMER_LEAP=y +CONFIG_IOMMU_IO_PGTABLE_FAST=y +# CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST is not set +CONFIG_ARM_SMMU=y +CONFIG_IOMMU_DEBUG=y +CONFIG_IOMMU_DEBUG_TRACKING=y +CONFIG_IOMMU_TESTS=y +CONFIG_QCOM_COMMON_LOG=y +CONFIG_MSM_SMEM=y +CONFIG_QPNP_HAPTIC=y +CONFIG_MSM_SMD=y +# CONFIG_MSM_SMD_DEBUG is not set +CONFIG_MSM_GLINK=y +CONFIG_MSM_GLINK_LOOPBACK_SERVER=y +CONFIG_MSM_GLINK_SMD_XPRT=y +CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_MSM_SPCOM=y +CONFIG_MSM_SPSS_UTILS=y +CONFIG_MSM_SMEM_LOGGING=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_RPM_SMD=y +CONFIG_QCOM_BUS_SCALING=y +CONFIG_MSM_SERVICE_LOCATOR=y +CONFIG_QCOM_DCC=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_SYSMON_GLINK_COMM=y +CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y +CONFIG_MSM_GLINK_PKT=y +CONFIG_MSM_SPM=y +CONFIG_QCOM_SCM=y +CONFIG_QCOM_WATCHDOG_V2=y +CONFIG_QCOM_IRQ_HELPER=y +CONFIG_QCOM_MEMORY_DUMP_V2=y +CONFIG_ICNSS=y +CONFIG_MSM_GLADIATOR_ERP_V2=y +CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y +CONFIG_MSM_GLADIATOR_HANG_DETECT=y +CONFIG_MSM_CORE_HANG_DETECT=y +CONFIG_MSM_RUN_QUEUE_STATS=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_QCOM_CPUSS_DUMP=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_PERFORMANCE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_PIL=y +CONFIG_MSM_PIL_SSR_GENERIC=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_TRACER_PKT=y +CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y +CONFIG_MSM_MPM_OF=y +CONFIG_MSM_EVENT_TIMER=y +CONFIG_MSM_AVTIMER=y +CONFIG_QCOM_REMOTEQDSS=y +CONFIG_MSM_SERVICE_NOTIFIER=y +CONFIG_MSM_QBT1000=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_RPM_LOG=y +CONFIG_MSM_RPM_STATS_LOG=y +CONFIG_QSEE_IPC_IRQ_BRIDGE=y +CONFIG_QCOM_SMCINVOKE=y +CONFIG_QCOM_EARLY_RANDOM=y +CONFIG_MEM_SHARE_QMI_SERVICE=y +CONFIG_QCOM_BIMC_BWMON=y +CONFIG_ARM_MEMLAT_MON=y +CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON=y +CONFIG_DEVFREQ_GOV_MEMLAT=y +CONFIG_QCOM_DEVFREQ_DEVBW=y +CONFIG_SPDM_SCM=y +CONFIG_DEVFREQ_SPDM=y +CONFIG_EXTCON=y +CONFIG_IIO=y +CONFIG_QCOM_RRADC=y +CONFIG_QCOM_TADC=y +CONFIG_PWM=y +CONFIG_PWM_QPNP=y +CONFIG_ARM_GIC_V3_ACL=y +CONFIG_PHY_XGENE=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_MSM_TZ_LOG=y +CONFIG_SENSORS_SSC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_FS_ICE_ENCRYPTION=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_EFIVAR_FS=y +CONFIG_ECRYPT_FS=y +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_SDCARD_FS=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_PAGE_OWNER=y +CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y +# CONFIG_SLUB_DEBUG_PANIC_ON is not set +# CONFIG_SLUB_DEBUG is not set +CONFIG_PAGE_POISONING=y +CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +# CONFIG_DEBUG_OBJECTS_RCU_HEAD is not set +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +# CONFIG_SLUB_DEBUG_ON is not set +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=5500 +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_WQ_WATCHDOG=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_PANIC_ON_SCHED_BUG=y +CONFIG_PANIC_ON_RT_THROTTLING=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_STACK_END_CHECK=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_FAULT_INJECTION=y +CONFIG_FAIL_PAGE_ALLOC=y +CONFIG_UFS_FAULT_INJECTION=y +CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y +CONFIG_IPC_LOGGING=y +CONFIG_QCOM_RTB=y +CONFIG_QCOM_RTB_SEPARATE_CPUS=y +CONFIG_FUNCTION_TRACER=y +CONFIG_PREEMPTIRQ_EVENTS=y +CONFIG_IRQSOFF_TRACER=y +CONFIG_PREEMPT_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_CPU_FREQ_SWITCH_PROFILER=y +CONFIG_LKDTM=y +CONFIG_MEMTEST=y +CONFIG_PANIC_ON_DATA_CORRUPTION=y +CONFIG_ARM64_PTDUMP=y +CONFIG_PID_IN_CONTEXTIDR=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_FREE_PAGES_RDONLY=y +CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y +CONFIG_CORESIGHT=y +CONFIG_CORESIGHT_EVENT=y +CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +CONFIG_CORESIGHT_SOURCE_ETM4X=y +CONFIG_CORESIGHT_REMOTE_ETM=y +CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0 +CONFIG_CORESIGHT_QCOM_REPLICATOR=y +CONFIG_CORESIGHT_STM=y +CONFIG_CORESIGHT_HWEVENT=y +CONFIG_CORESIGHT_CTI=y +CONFIG_CORESIGHT_TPDA=y +CONFIG_CORESIGHT_TPDM=y +CONFIG_CORESIGHT_QPDI=y +CONFIG_CORESIGHT_SOURCE_DUMMY=y +CONFIG_PFK=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_ECHAINIV=y +CONFIG_CRYPTO_XCBC=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DEV_QCRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y +CONFIG_CRYPTO_DEV_QCEDEV=y +CONFIG_CRYPTO_DEV_OTA_CRYPTO=y +CONFIG_CRYPTO_DEV_QCOM_ICE=y +CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem" +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y +CONFIG_CRYPTO_CRC32_ARM64=y +CONFIG_XZ_DEC=y +CONFIG_QMI_ENCDEC=y +CONFIG_TRI_STATE_KEY=y +CONFIG_RF_CABLE_DETECT=y +#CONFIG_DEVMEM=y +# CONFIG_DEVMEM is not set +CONFIG_LEDS_GPIO=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_DEVICE_INFO=y +CONFIG_PSTORE_PMSG=y +CONFIG_LEDS_CLASS=y +CONFIG_KSWAPD_CPU_AFFINITY_MASK="f" diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 570a21b464ca..6ce1ec629f13 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -240,7 +240,9 @@ CONFIG_CFG80211_CERTIFICATION_ONUS=y CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y CONFIG_RFKILL=y -CONFIG_NFC_NQ=y +# CONFIG_NFC_NQ=y +CONFIG_NFC_PN5XX=y +CONFIG_NFC_PN80T=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y @@ -254,6 +256,7 @@ CONFIG_QSEECOM=y CONFIG_HDCP_QSEECOM=y CONFIG_UID_SYS_STATS=y CONFIG_QPNP_MISC=y +CONFIG_DLOAD_MODE_DEFAULT=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y @@ -306,21 +309,25 @@ CONFIG_INPUT_KEYRESET=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI=y +CONFIG_INPUT_FINGERPRINT=y +CONFIG_FINGERPRINT_DETECT=y +CONFIG_FINGERPRINT_FPC=y +CONFIG_FINGERPRINT_GOODIX=y CONFIG_SECURE_TOUCH=y -CONFIG_TOUCHSCREEN_ST=y -CONFIG_TOUCHSCREEN_ST_I2C=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_UINPUT=y -CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SERIAL_MSM_HS=y CONFIG_SERIAL_MSM_SMD=y CONFIG_DIAG_CHAR=y @@ -347,6 +354,8 @@ CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_QPNP_FG_GEN3=y +CONFIG_FG_BQ27541=y +CONFIG_ONEPLUS_FASTCHG=y CONFIG_MSM_BCL_CTL=y CONFIG_MSM_BCL_PERIPHERAL_CTL=y CONFIG_BATTERY_BCL=y @@ -624,6 +633,8 @@ CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_ECRYPT_FS=y @@ -678,3 +689,15 @@ CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_QMI_ENCDEC=y +CONFIG_TRI_STATE_KEY=y + +CONFIG_RF_CABLE_DETECT=y +CONFIG_LEDS_GPIO=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_DEVICE_INFO=y +CONFIG_PSTORE_PMSG=y +CONFIG_LEDS_CLASS=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_KSWAPD_CPU_AFFINITY_MASK="f" diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index 7015528d0a3c..e0fbe1c16f51 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -43,10 +43,10 @@ CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_MODULE_SIG=y -CONFIG_MODULE_SIG_FORCE=y -CONFIG_MODULE_SIG_SHA512=y +# CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE=y +# CONFIG_MODULE_SIG_SHA512=y # CONFIG_BLK_DEV_BSG is not set CONFIG_PARTITION_ADVANCED=y # CONFIG_IOSCHED_DEADLINE is not set @@ -81,7 +81,7 @@ CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set CONFIG_PM_DEBUG=y CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -243,7 +243,9 @@ CONFIG_CFG80211_REG_CELLULAR_HINTS=y CONFIG_CFG80211_INTERNAL_REGDB=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_RFKILL=y -CONFIG_NFC_NQ=y +# CONFIG_NFC_NQ=y +CONFIG_NFC_PN5XX=y +CONFIG_NFC_PN80T=y CONFIG_IPC_ROUTER=y CONFIG_IPC_ROUTER_SECURITY=y CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y @@ -309,18 +311,20 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TOUCHSCREEN=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21=y -CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21=y +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI=y +CONFIG_INPUT_FINGERPRINT=y +CONFIG_FINGERPRINT_DETECT=y +CONFIG_FINGERPRINT_FPC=y +CONFIG_FINGERPRINT_GOODIX=y CONFIG_SECURE_TOUCH=y -CONFIG_TOUCHSCREEN_ST=y -CONFIG_TOUCHSCREEN_ST_I2C=y CONFIG_INPUT_MISC=y CONFIG_INPUT_HBTP_INPUT=y CONFIG_INPUT_QPNP_POWER_ON=y CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y -CONFIG_INPUT_STMVL53L0=y # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set @@ -354,6 +358,11 @@ CONFIG_QCOM_DLOAD_MODE=y CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y CONFIG_QPNP_FG_GEN3=y +CONFIG_FG_BQ27541=y +CONFIG_ONEPLUS_FASTCHG=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_HID_SONY=y +CONFIG_SONY_FF=y CONFIG_MSM_BCL_CTL=y CONFIG_MSM_BCL_PERIPHERAL_CTL=y CONFIG_BATTERY_BCL=y @@ -516,12 +525,13 @@ CONFIG_EDAC=y CONFIG_EDAC_MM_EDAC=y CONFIG_EDAC_CORTEX_ARM64=y CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y +CONFIG_PAGE_POISONING=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_QPNP=y CONFIG_ESOC=y CONFIG_ESOC_DEV=y CONFIG_ESOC_CLIENT=y -CONFIG_ESOC_DEBUG=y +# CONFIG_ESOC_DEBUG is not set CONFIG_ESOC_MDM_4x=y CONFIG_ESOC_MDM_DRV=y CONFIG_ESOC_MDM_DBG_ENG=y @@ -547,14 +557,14 @@ CONFIG_RMNET_IPA3=y CONFIG_GPIO_USB_DETECT=y CONFIG_MSM_MHI=y CONFIG_MSM_MHI_UCI=y -CONFIG_MSM_MHI_DEBUG=y +# CONFIG_MSM_MHI_DEBUG is not set CONFIG_SEEMP_CORE=y CONFIG_USB_BAM=y CONFIG_MSM_MDSS_PLL=y CONFIG_REMOTE_SPINLOCK_MSM=y CONFIG_MSM_TIMER_LEAP=y CONFIG_IOMMU_IO_PGTABLE_FAST=y -CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y +# CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST is not set CONFIG_ARM_SMMU=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y @@ -563,7 +573,7 @@ CONFIG_QCOM_COMMON_LOG=y CONFIG_MSM_SMEM=y CONFIG_QPNP_HAPTIC=y CONFIG_MSM_SMD=y -CONFIG_MSM_SMD_DEBUG=y +# CONFIG_MSM_SMD_DEBUG is not set CONFIG_MSM_GLINK=y CONFIG_MSM_GLINK_LOOPBACK_SERVER=y CONFIG_MSM_GLINK_SMD_XPRT=y @@ -651,6 +661,8 @@ CONFIG_QFMT_V2=y CONFIG_FUSE_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_EFIVAR_FS=y @@ -667,18 +679,19 @@ CONFIG_PAGE_OWNER=y CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y -CONFIG_SLUB_DEBUG_PANIC_ON=y +# CONFIG_SLUB_DEBUG_PANIC_ON is not set +# CONFIG_SLUB_DEBUG is not set CONFIG_PAGE_POISONING=y CONFIG_PAGE_POISONING_ENABLE_DEFAULT=y -CONFIG_DEBUG_OBJECTS=y -CONFIG_DEBUG_OBJECTS_FREE=y -CONFIG_DEBUG_OBJECTS_TIMERS=y -CONFIG_DEBUG_OBJECTS_WORK=y -CONFIG_DEBUG_OBJECTS_RCU_HEAD=y -CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y -CONFIG_SLUB_DEBUG_ON=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_OBJECTS_FREE is not set +# CONFIG_DEBUG_OBJECTS_TIMERS is not set +# CONFIG_DEBUG_OBJECTS_WORK is not set +# CONFIG_DEBUG_OBJECTS_RCU_HEAD is not set +# CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER is not set +# CONFIG_SLUB_DEBUG_ON is not set CONFIG_DEBUG_KMEMLEAK=y -CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000 +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=5500 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_MEMORY_INIT=y @@ -759,3 +772,15 @@ CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_XZ_DEC=y CONFIG_QMI_ENCDEC=y +CONFIG_TRI_STATE_KEY=y +CONFIG_RF_CABLE_DETECT=y +#CONFIG_DEVMEM=y +# CONFIG_DEVMEM is not set +CONFIG_LEDS_GPIO=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_DEVICE_INFO=y +CONFIG_PSTORE_PMSG=y +CONFIG_LEDS_CLASS=y +CONFIG_KSWAPD_CPU_AFFINITY_MASK="f" diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 3112c2a9d96f..0af430a74507 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -117,9 +117,16 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr) LOG_BARRIER; \ }) +/*2017-08-22 add for dash adapter update*/ +#define __raw_write_logged_dash(v, a, _t) ({ \ + volatile void __iomem *_a = (a); \ + __raw_write##_t##_no_log((v), _a); \ + }) #define __raw_writeb(v, a) __raw_write_logged((v), a, b) #define __raw_writew(v, a) __raw_write_logged((v), a, w) #define __raw_writel(v, a) __raw_write_logged((v), a, l) +/*2017-08-22 add for dash adapter update*/ +#define __raw_writel_dash(v, a) __raw_write_logged_dash((v), a, l) #define __raw_writeq(v, a) __raw_write_logged((v), a, q) #define __raw_read_logged(a, _l, _t) ({ \ @@ -135,9 +142,17 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr) __a; \ }) +/*2017-08-22 add for dash adapter update*/ +#define __raw_read_logged_dash(a, _l, _t) ({ \ + _t __a; \ + const volatile void __iomem *_a = (const volatile void __iomem *)(a); \ + __a = __raw_read##_l##_no_log(_a); \ + }) #define __raw_readb(a) __raw_read_logged((a), b, u8) #define __raw_readw(a) __raw_read_logged((a), w, u16) #define __raw_readl(a) __raw_read_logged((a), l, u32) +/*2017-08-22 add for dash adapter update*/ +#define __raw_readl_dash(a) __raw_read_logged_dash((a), l, u32) #define __raw_readq(a) __raw_read_logged((a), q, u64) /* IO barriers */ @@ -154,11 +169,19 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr) #define readb_relaxed(c) ({ u8 __r = __raw_readb(c); __r; }) #define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16)__raw_readw(c)); __r; }) #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; }) +/*2017-08-22 add for dash adapter update*/ +#define readl_relaxed_dash(c) ({ \ + u32 __r = le32_to_cpu((__force __le32)__raw_readl_dash(c)); \ + __r; }) #define readq_relaxed(c) ({ u64 __r = le64_to_cpu((__force __le64)__raw_readq(c)); __r; }) #define writeb_relaxed(v,c) ((void)__raw_writeb((v),(c))) #define writew_relaxed(v,c) ((void)__raw_writew((__force u16)cpu_to_le16(v),(c))) #define writel_relaxed(v,c) ((void)__raw_writel((__force u32)cpu_to_le32(v),(c))) +/*2017-08-22 add for dash adapter update*/ +#define writel_relaxed_dash(v, c) ( \ + (void)__raw_writel_dash((__force u32)cpu_to_le32(v),\ + (c))) #define writeq_relaxed(v,c) ((void)__raw_writeq((__force u64)cpu_to_le64(v),(c))) #define readb_relaxed_no_log(c) ({ u8 __v = __raw_readb_no_log(c); __v; }) @@ -179,11 +202,15 @@ static inline u64 __raw_readq_no_log(const volatile void __iomem *addr) #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) +/*2017-08-22 add for dash adapter update*/ +#define readl_dash(c) ({ u32 __v = readl_relaxed_dash(c); __v; }) #define readq(c) ({ u64 __v = readq_relaxed(c); __iormb(); __v; }) #define writeb(v,c) ({ __iowmb(); writeb_relaxed((v),(c)); }) #define writew(v,c) ({ __iowmb(); writew_relaxed((v),(c)); }) #define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c)); }) +/*2017-08-22 add for dash adapter update*/ +#define writel_dash(v, c) ({ writel_relaxed_dash((v), (c)); }) #define writeq(v,c) ({ __iowmb(); writeq_relaxed((v),(c)); }) #define readb_no_log(c) ({ u8 __v = readb_relaxed_no_log(c); __iormb(); __v; }) diff --git a/drivers/Kconfig b/drivers/Kconfig index 4051a164c2eb..f815d7e86db3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -214,4 +214,7 @@ source "drivers/sensors/Kconfig" source "drivers/tee/Kconfig" +source "drivers/param_read_write/Kconfig" + +source "drivers/oneplus/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index d563f5c13544..f950dfe80217 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -184,3 +184,8 @@ obj-$(CONFIG_BIF) += bif/ obj-$(CONFIG_SENSORS_SSC) += sensors/ obj-$(CONFIG_ESOC) += esoc/ obj-$(CONFIG_TEE) += tee/ + + +obj-y += param_read_write/ + +obj-$(CONFIG_OEM_DEBUG_SUPPORT) += oneplus/ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 623094457788..3007b28206d7 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -70,7 +70,7 @@ #include #include #include - +#include #include #include "binder_alloc.h" #include "binder_trace.h" @@ -5766,6 +5766,52 @@ BINDER_DEBUG_ENTRY(stats); BINDER_DEBUG_ENTRY(transactions); BINDER_DEBUG_ENTRY(transaction_log); +static int proc_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, binder_state_show, inode->i_private); +} + +static int proc_transactions_open(struct inode *inode, struct file *file) +{ + return single_open(file, binder_transactions_show, inode->i_private); +} + +static int proc_transaction_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, binder_transaction_log_show, inode->i_private); +} + +static const struct file_operations proc_state_operations = { + .open = proc_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations proc_transactions_operations = { + .open = proc_transactions_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations proc_transaction_log_operations = { + .open = proc_transaction_log_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int binder_proc_init(void) +{ + proc_create("proc_state", 0, NULL, + &proc_state_operations); + proc_create("proc_transactions", 0, NULL, + &proc_transactions_operations); + proc_create("proc_transaction_log", 0, NULL, + &proc_transaction_log_operations); + return 0; +} static int __init init_binder_device(const char *name) { int ret; @@ -5861,7 +5907,7 @@ static int __init binder_init(void) if (ret) goto err_init_binder_device_failed; } - + binder_proc_init(); return ret; err_init_binder_device_failed: diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c1093c0d4dea..5295ea9b9730 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -312,6 +312,7 @@ static const char * const fw_path[] = { "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, "/lib/firmware", + "/vendor/etc/firmware", "/lib64/firmware" }; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 7af116e12e53..5b80939c63f7 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "power.h" @@ -68,6 +69,10 @@ static struct wakeup_source deleted_ws = { .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), }; +#define WORK_TIMEOUT (60*1000) +static void ws_printk(struct work_struct *work); +static DECLARE_DELAYED_WORK(ws_printk_work, ws_printk); + /** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare. @@ -866,6 +871,24 @@ void pm_print_active_wakeup_sources(void) } EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); +static void ws_printk(struct work_struct *work) +{ + pm_print_active_wakeup_sources(); + queue_delayed_work(system_freezable_wq, &ws_printk_work, + msecs_to_jiffies(WORK_TIMEOUT)); +} + +void pm_print_active_wakeup_sources_queue(bool on) +{ + if (on) { + queue_delayed_work(system_freezable_wq, &ws_printk_work, + msecs_to_jiffies(WORK_TIMEOUT)); + } else { + cancel_delayed_work(&ws_printk_work); + } +} +EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources_queue); + /** * pm_wakeup_pending - Check if power transition in progress should be aborted. * diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index 0a61186167ba..0a00f76b880f 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -519,10 +519,11 @@ static int btfm_slim_remove(struct slim_device *slim) BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd"); slim_remove_device(&btfm_slim->slim_ifd); + kfree(btfm_slim); + BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd"); slim_remove_device(slim); - kfree(btfm_slim); return 0; } diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 803b7840759a..e4699fe7f842 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -59,7 +59,7 @@ static DEFINE_MUTEX(misc_mtx); /* * Assigned numbers, used for dynamic minors */ -#define DYNAMIC_MINORS 96 /* like dynamic majors */ +#define DYNAMIC_MINORS 128 /* like dynamic majors */ static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); #ifdef CONFIG_PROC_FS diff --git a/drivers/clk/msm/clock-gcc-8998.c b/drivers/clk/msm/clock-gcc-8998.c index b1c8cc43769f..db56935fd925 100644 --- a/drivers/clk/msm/clock-gcc-8998.c +++ b/drivers/clk/msm/clock-gcc-8998.c @@ -73,6 +73,7 @@ static DEFINE_CLK_VOTER(bimc_msmbus_clk, &bimc_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(bimc_msmbus_a_clk, &bimc_a_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(cnoc_msmbus_clk, &cnoc_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(uart_cnoc_msmbus_a_clk, &cnoc_a_clk.c, LONG_MAX); static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, &cxo_clk_src.c); static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &cxo_clk_src.c); static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &cxo_clk_src.c); @@ -120,6 +121,7 @@ DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(rf_clk3_pin, rf_clk3_pin_ao, static DEFINE_CLK_VOTER(scm_ce1_clk, &ce1_clk.c, 85710000); static DEFINE_CLK_VOTER(snoc_msmbus_clk, &snoc_clk.c, LONG_MAX); static DEFINE_CLK_VOTER(snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX); +static DEFINE_CLK_VOTER(uart_snoc_msmbus_a_clk, &snoc_a_clk.c, LONG_MAX); DEFINE_CLK_DUMMY(gcc_ce1_ahb_m_clk, 0); DEFINE_CLK_DUMMY(gcc_ce1_axi_m_clk, 0); @@ -2427,6 +2429,7 @@ static struct clk_lookup msm_clocks_rpm_8998[] = { CLK_LIST(ce1_a_clk), CLK_LIST(cnoc_msmbus_clk), CLK_LIST(cnoc_msmbus_a_clk), + CLK_LIST(uart_cnoc_msmbus_a_clk), CLK_LIST(cxo_clk_src_ao), CLK_LIST(cxo_dwc3_clk), CLK_LIST(cxo_lpm_clk), @@ -2481,6 +2484,7 @@ static struct clk_lookup msm_clocks_rpm_8998[] = { CLK_LIST(rf_clk3_pin_ao), CLK_LIST(scm_ce1_clk), CLK_LIST(snoc_msmbus_clk), + CLK_LIST(uart_snoc_msmbus_a_clk), CLK_LIST(snoc_msmbus_a_clk), CLK_LIST(gcc_ce1_ahb_m_clk), CLK_LIST(gcc_ce1_axi_m_clk), diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c index cb7323886082..0a2aada16a91 100644 --- a/drivers/cpufreq/qcom-cpufreq.c +++ b/drivers/cpufreq/qcom-cpufreq.c @@ -42,6 +42,32 @@ struct cpufreq_suspend_t { }; static DEFINE_PER_CPU(struct cpufreq_suspend_t, suspend_data); +#define LITTLE_CPU_QOS_FREQ 1900800 +#define BIG_CPU_QOS_FREQ 2361600 + +unsigned int cluster1_first_cpu; +static bool qos_cpufreq_flag; +static bool c1_cpufreq_update_flag; +static void c0_cpufreq_limit(struct work_struct *work); +static void c1_cpufreq_limit(struct work_struct *work); +static struct workqueue_struct *qos_cpufreq_work_queue; +static DECLARE_WORK(c0_cpufreq_limit_work, c0_cpufreq_limit); +static DECLARE_WORK(c1_cpufreq_limit_work, c1_cpufreq_limit); +struct qos_request_value { + bool flag; + unsigned int max_cpufreq; + unsigned int min_cpufreq; +}; +static struct qos_request_value c0_qos_request_value = { + .flag = false, + .max_cpufreq = INT_MAX, + .min_cpufreq = MIN_CPUFREQ, +}; +static struct qos_request_value c1_qos_request_value = { + .flag = false, + .max_cpufreq = INT_MAX, + .min_cpufreq = MIN_CPUFREQ, +}; static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq, unsigned int index) @@ -77,14 +103,22 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, mutex_lock(&per_cpu(suspend_data, policy->cpu).suspend_mutex); - if (target_freq == policy->cur) - goto done; + if (target_freq == policy->cur) { + if (c1_cpufreq_update_flag) + c1_cpufreq_update_flag = false; + else + goto done; + } if (per_cpu(suspend_data, policy->cpu).device_suspended) { - pr_debug("cpufreq: cpu%d scheduling frequency change " + if (likely(qos_cpufreq_flag)) { + qos_cpufreq_flag = false; + } else { + pr_debug("cpufreq: cpu%d scheduling frequency change " "in suspend.\n", policy->cpu); - ret = -EFAULT; - goto done; + ret = -EFAULT; + goto done; + } } table = cpufreq_frequency_get_table(policy->cpu); @@ -94,6 +128,17 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, ret = -ENODEV; goto done; } + + if (cluster1_first_cpu) { + if (policy->cpu >= cluster1_first_cpu) { + target_freq = min(c1_qos_request_value.max_cpufreq, target_freq); + target_freq = max(c1_qos_request_value.min_cpufreq, target_freq); + }else { + target_freq = min(c0_qos_request_value.max_cpufreq, target_freq); + target_freq = max(c0_qos_request_value.min_cpufreq, target_freq); + } + } + if (cpufreq_frequency_table_target(policy, table, target_freq, relation, &index)) { pr_err("cpufreq: invalid target_freq: %d\n", target_freq); @@ -443,6 +488,10 @@ static int __init msm_cpufreq_probe(struct platform_device *pdev) devm_kfree(dev, ftbl); } ftbl = per_cpu(freq_table, cpu - 1); + } else { + if(!IS_ERR(ftbl)) + cluster1_first_cpu = cpu; + //pr_info("cluster1_first_cpu: %d",cluster1_first_cpu); } per_cpu(freq_table, cpu) = ftbl; } @@ -463,6 +512,231 @@ static struct platform_driver msm_cpufreq_plat_driver = { }, }; +static int get_c0_available_cpufreq(void) +{ + unsigned int max_cpufreq_index, min_cpufreq_index; + unsigned int max_index; + unsigned int index_max, index_min; + struct cpufreq_frequency_table *table, *pos; + + table = cpufreq_frequency_get_table(0); + if (!table) { + pr_err("cpufreq: Failed to get frequency table for CPU%u\n",0); + return -EINVAL; + } + + max_cpufreq_index = (unsigned int)pm_qos_request(PM_QOS_C0_CPUFREQ_MAX); + min_cpufreq_index = (unsigned int)pm_qos_request(PM_QOS_C0_CPUFREQ_MIN); + /* you can limit the min cpufreq*/ + if (min_cpufreq_index > max_cpufreq_index) + max_cpufreq_index = min_cpufreq_index; + + /*get the available cpufreq*/ + /* lock for the max available cpufreq*/ + cpufreq_for_each_valid_entry(pos, table) { + max_index = pos - table; + } + if (max_cpufreq_index & MASK_CPUFREQ) { + index_max = MAX_CPUFREQ - max_cpufreq_index; + if (index_max> max_index) + index_max = 0; + index_max = max_index - index_max; + } else { + if (max_cpufreq_index > max_index) + index_max = max_index; + } + if (min_cpufreq_index & MASK_CPUFREQ) { + index_min = MAX_CPUFREQ - min_cpufreq_index; + if (index_min > max_index) + index_min = 0; + index_min = max_index - index_min; + } else { + if (min_cpufreq_index > max_index) + index_min = max_index; + } + c0_qos_request_value.max_cpufreq = table[index_max].frequency; + c0_qos_request_value.min_cpufreq = table[index_min].frequency; + pr_debug("::: m:%d, ii:%d-, mm:%d-",max_index, index_min,index_max); + + return 0; +} + +static int get_c1_available_cpufreq(void) +{ + unsigned int max_cpufreq_index, min_cpufreq_index; + unsigned int max_index; + unsigned int index_max, index_min; + struct cpufreq_frequency_table *table, *pos; + + table = cpufreq_frequency_get_table(cluster1_first_cpu); + if (!table) { + pr_err("cpufreq: Failed to get frequency table for CPU%u\n", + cluster1_first_cpu); + return -EINVAL; + } + + max_cpufreq_index = (unsigned int)pm_qos_request(PM_QOS_C1_CPUFREQ_MAX); + min_cpufreq_index = (unsigned int)pm_qos_request(PM_QOS_C1_CPUFREQ_MIN); + /* you can limit the min cpufreq*/ + if (min_cpufreq_index > max_cpufreq_index) + max_cpufreq_index = min_cpufreq_index; + + /*get the available cpufreq*/ + /* lock for the max available cpufreq*/ + cpufreq_for_each_valid_entry(pos, table) { + max_index = pos - table; + } + /* add limits */ + if (max_cpufreq_index & MASK_CPUFREQ) { + index_max = MAX_CPUFREQ - max_cpufreq_index; + if (index_max> max_index) + index_max = 0; + index_max = max_index - index_max; + } else { + if (max_cpufreq_index > max_index) + index_max = max_index; + } + if (min_cpufreq_index & MASK_CPUFREQ) { + index_min = MAX_CPUFREQ - min_cpufreq_index; + if (index_min > max_index) + index_min = 0; + index_min = max_index - index_min; + } else { + if (min_cpufreq_index > max_index) + index_min = max_index; + } + c1_qos_request_value.max_cpufreq = table[index_max].frequency; + c1_qos_request_value.min_cpufreq = table[index_min].frequency; + pr_debug("::: m:%d, ii:%d-, mm:%d-",max_index, index_min,index_max); + + return 0; +} + +static int c0_cpufreq_qos_handler(struct notifier_block *b, unsigned long val, void *v) +{ + struct cpufreq_policy *policy; + int ret = -1; + + //get_online_cpus(); + policy = cpufreq_cpu_get(0); + + if (!policy) + return NOTIFY_BAD; + + if (!policy->governor) { + cpufreq_cpu_put(policy); + return NOTIFY_BAD; + } + + if (strcmp(policy->governor->name, "interactive")) { + cpufreq_cpu_put(policy); + return NOTIFY_OK; + } + + ret = get_c0_available_cpufreq(); + if (!ret) { + cpufreq_cpu_put(policy); + return NOTIFY_BAD; + } + + __cpufreq_driver_target(policy, + c0_qos_request_value.min_cpufreq, CPUFREQ_RELATION_H); + + cpufreq_cpu_put(policy); + //put_online_cpus(); + return NOTIFY_OK; +} + +static struct notifier_block c0_cpufreq_qos_notifier = { + .notifier_call = c0_cpufreq_qos_handler, +}; + +static int c1_cpufreq_qos_handler(struct notifier_block *b, unsigned long val, void *v) +{ + struct cpufreq_policy *policy; + int ret = -1; + + pr_info(":::update_policy\n"); + /* in use, policy may be NULL, because hotplug can close first cpu core*/ + //get_online_cpus(); + policy = cpufreq_cpu_get(cluster1_first_cpu); + + if (!policy) + return NOTIFY_BAD; + + if (!policy->governor) { + cpufreq_cpu_put(policy); + return NOTIFY_BAD; + } + + if (strcmp(policy->governor->name, "interactive")) { + cpufreq_cpu_put(policy); + return NOTIFY_OK; + } + + ret = get_c1_available_cpufreq(); + if (ret) { + cpufreq_cpu_put(policy); + return NOTIFY_BAD; + } + + c1_cpufreq_update_flag = true; + __cpufreq_driver_target(policy, + c1_qos_request_value.min_cpufreq, CPUFREQ_RELATION_H); + + //__cpufreq_driver_target(policy, val, CPUFREQ_RELATION_H); + cpufreq_cpu_put(policy); + + //put_online_cpus(); + return NOTIFY_OK; +} + +static struct notifier_block c1_cpufreq_qos_notifier = { + .notifier_call = c1_cpufreq_qos_handler, +}; + +static void c0_cpufreq_limit(struct work_struct *work) +{ + struct cpufreq_policy *policy; + + policy = cpufreq_cpu_get(0); + if (policy) { + qos_cpufreq_flag = true; + cpufreq_driver_target(policy, + LITTLE_CPU_QOS_FREQ, CPUFREQ_RELATION_H); + cpufreq_cpu_put(policy); + } + sched_set_boost(1); +} + +void c0_cpufreq_limit_queue(void) +{ + if (qos_cpufreq_work_queue) + queue_work(qos_cpufreq_work_queue, &c0_cpufreq_limit_work); +} +EXPORT_SYMBOL_GPL(c0_cpufreq_limit_queue); + +static void c1_cpufreq_limit(struct work_struct *work) +{ + struct cpufreq_policy *policy; + + policy = cpufreq_cpu_get(cluster1_first_cpu); + if (policy) { + qos_cpufreq_flag = true; + cpufreq_driver_target(policy, + BIG_CPU_QOS_FREQ, CPUFREQ_RELATION_H); + cpufreq_cpu_put(policy); + } + +} + +void c1_cpufreq_limit_queue(void) +{ + if (qos_cpufreq_work_queue) + queue_work(qos_cpufreq_work_queue, &c1_cpufreq_limit_work); +} +EXPORT_SYMBOL_GPL(c1_cpufreq_limit_queue); + static int __init msm_cpufreq_register(void) { int cpu, rc; @@ -482,6 +756,15 @@ static int __init msm_cpufreq_register(void) suspend_mutex)); return rc; } + /* add cpufreq qos notify */ + pm_qos_add_notifier(PM_QOS_C0_CPUFREQ_MAX, &c0_cpufreq_qos_notifier); + pm_qos_add_notifier(PM_QOS_C0_CPUFREQ_MIN, &c0_cpufreq_qos_notifier); + pm_qos_add_notifier(PM_QOS_C1_CPUFREQ_MAX, &c1_cpufreq_qos_notifier); + pm_qos_add_notifier(PM_QOS_C1_CPUFREQ_MIN, &c1_cpufreq_qos_notifier); + + qos_cpufreq_work_queue = create_singlethread_workqueue("qos_cpufreq"); + if (qos_cpufreq_work_queue == NULL) + pr_info("%s: failed to create work queue", __func__); register_pm_notifier(&msm_cpufreq_pm_notifier); return cpufreq_register_driver(&msm_cpufreq_driver); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 324cce5d7354..812d93e39fcc 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -157,6 +157,11 @@ static bool sleep_disabled; module_param_named(sleep_disabled, sleep_disabled, bool, S_IRUGO | S_IWUSR | S_IWGRP); +void msm_cpuidle_set_sleep_disable(bool disable) +{ + sleep_disabled = disable; +} + s32 msm_cpuidle_get_deep_idle_latency(void) { return 10; diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index 68b6a26f00b8..e58350669e82 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -125,9 +125,6 @@ static int qti_ice_setting_config(struct request *req, return -EPERM; } - if (!setting) - return -EINVAL; - if ((short)(crypto_data->key_index) >= 0) { memcpy(&setting->crypto_data, crypto_data, @@ -218,6 +215,8 @@ static int qcom_ice_bus_register(struct ice_device *ice_dev) } err = 0; + /* register again only if we didn't register previously */ + if (!ice_dev->bus_vote.client_handle) { ice_dev->bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); if (!ice_dev->bus_vote.client_handle) { @@ -225,6 +224,7 @@ static int qcom_ice_bus_register(struct ice_device *ice_dev) __func__); err = -EFAULT; goto out; + } } /* cache the vote index for minimum and maximum bandwidth */ @@ -1615,8 +1615,6 @@ static struct ice_device *get_ice_device_from_storage_type list_for_each_entry(ice_dev, &ice_devices, list) { if (!strcmp(ice_dev->ice_instance_type, storage_type)) { - pr_debug("%s: found ice device %pK\n", - __func__, ice_dev); return ice_dev; } } diff --git a/drivers/devfreq/devfreq_devbw.c b/drivers/devfreq/devfreq_devbw.c index 8a836d92e542..87f3f6d1c65e 100644 --- a/drivers/devfreq/devfreq_devbw.c +++ b/drivers/devfreq/devfreq_devbw.c @@ -35,6 +35,20 @@ #define MAX_PATHS 2 #define DBL_BUF 2 +#include +struct qos_request_v { + int max_state; + int max_devfreq; + int min_devfreq; +}; + +static bool cpubw_flag = false; +static struct qos_request_v qos_request_value = { + .max_state = 0, + .max_devfreq = INT_MAX, + .min_devfreq = 0, +}; + struct dev_data { struct msm_bus_vectors vectors[MAX_PATHS * DBL_BUF]; struct msm_bus_paths bw_levels[DBL_BUF]; @@ -106,6 +120,53 @@ static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, *freq = atleast; } +static void find_freq_cpubw(struct devfreq_dev_profile *p, unsigned long *freq, + u32 flags) +{ + int i; + unsigned long atmost, atleast, f; + int min_index, max_index; + + if (cpubw_flag) { + min_index = qos_request_value.min_devfreq; + if (p->max_state > qos_request_value.max_devfreq) + max_index = qos_request_value.max_devfreq; + else + max_index = p->max_state; + } else { + min_index = 0; + max_index = p->max_state; + } + + atmost = p->freq_table[min_index]; + atleast = p->freq_table[max_index-1]; + + for (i = min_index; i < max_index; i++) { + f = p->freq_table[i]; + if (f <= *freq) + atmost = max(f, atmost); + if (f >= *freq) + atleast = min(f, atleast); + } + + if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) + *freq = atmost; + else + *freq = atleast; +} + +static int devbw_target_cpubw(struct device *dev, unsigned long *freq, u32 flags) +{ + struct dev_data *d = dev_get_drvdata(dev); + + find_freq_cpubw(&d->dp, freq, flags); + + if (!d->gov_ab) + return set_bw(dev, *freq, find_ab(d, freq)); + else + return set_bw(dev, *freq, d->gov_ab); +} + static int devbw_target(struct device *dev, unsigned long *freq, u32 flags) { struct dev_data *d = dev_get_drvdata(dev); @@ -127,6 +188,43 @@ static int devbw_get_dev_status(struct device *dev, return 0; } +static int devfreq_qos_handler(struct notifier_block *b, unsigned long val, void *v) +{ + unsigned int max_devfreq_index, min_devfreq_index; + unsigned int index_max, index_min; + + max_devfreq_index = (unsigned int)pm_qos_request(PM_QOS_DEVFREQ_MAX); + min_devfreq_index = (unsigned int)pm_qos_request(PM_QOS_DEVFREQ_MIN); + + /* add limit */ + if (max_devfreq_index & MASK_CPUFREQ) { + index_max = MAX_CPUFREQ - max_devfreq_index; + if (index_max > qos_request_value.max_state) + index_max = 0; + index_max = qos_request_value.max_state - index_max; + } else { + if (max_devfreq_index > qos_request_value.max_state) + index_max = qos_request_value.max_state; + } + if (min_devfreq_index & MASK_CPUFREQ) { + index_min = MAX_CPUFREQ - min_devfreq_index; + if (index_min > (qos_request_value.max_state-1)) + index_min = 0; + index_min = qos_request_value.max_state -1 - index_min; + } else { + if (min_devfreq_index > qos_request_value.max_state) + index_min = qos_request_value.max_state -1; + } + + qos_request_value.min_devfreq = index_min; + qos_request_value.max_devfreq = index_max; + + return NOTIFY_OK; +} +static struct notifier_block devfreq_qos_notifier = { + .notifier_call = devfreq_qos_handler, +}; + #define PROP_PORTS "qcom,src-dst-ports" #define PROP_TBL "qcom,bw-tbl" #define PROP_AB_PER "qcom,ab-percent" @@ -182,7 +280,11 @@ int devfreq_add_devbw(struct device *dev) p = &d->dp; p->polling_ms = 50; - p->target = devbw_target; + if (strstr(d->bw_data.name, "soc:qcom,cpubw") != NULL) { + p->target = devbw_target_cpubw; + cpubw_flag = true; + } else + p->target = devbw_target; p->get_dev_status = devbw_get_dev_status; if (of_find_property(dev->of_node, PROP_TBL, &len)) { @@ -231,6 +333,11 @@ int devfreq_add_devbw(struct device *dev) return PTR_ERR(d->df); } + if (cpubw_flag) { + qos_request_value.max_state = len; + qos_request_value.min_devfreq = 0; + qos_request_value.max_devfreq = len; + } return 0; } @@ -281,6 +388,10 @@ static struct platform_driver devbw_driver = { static int __init devbw_init(void) { + /* add cpufreq qos notify */ + cpubw_flag = false; + pm_qos_add_notifier(PM_QOS_DEVFREQ_MAX, &devfreq_qos_notifier); + pm_qos_add_notifier(PM_QOS_DEVFREQ_MIN, &devfreq_qos_notifier); platform_driver_register(&devbw_driver); return 0; } diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index db8f0eeca7c9..e674d536f74d 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -273,8 +273,10 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) unsigned long flags; bool attached; - if (!edev) + if (!edev) { + dev_err(&edev->dev, "enval err\n"); return -EINVAL; + } spin_lock_irqsave(&edev->lock, flags); @@ -284,6 +286,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) if (check_mutually_exclusive(edev, (edev->state & ~mask) | (state & mask))) { spin_unlock_irqrestore(&edev->lock, flags); + dev_err(&edev->dev, "out because of mutually_exclusive\n"); return -EPERM; } @@ -293,9 +296,12 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) for (index = 0; index < edev->max_supported; index++) { if (is_extcon_changed(old_state, edev->state, index, - &attached)) + &attached)) { + dev_err(&edev->dev, "index=%d,attached=%d\n", + index, attached); raw_notifier_call_chain(&edev->nh[index], attached, edev); + } } /* This could be in interrupt handler */ @@ -412,15 +418,21 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, u32 state; int index; - if (!edev) + if (!edev) { + dev_err(&edev->dev, "dev enval err\n"); return -EINVAL; + } index = find_cable_index_by_id(edev, id); - if (index < 0) + if (index < 0) { + dev_err(&edev->dev, "index is valid\n"); return index; + } - if (edev->max_supported && edev->max_supported <= index) + if (edev->max_supported && edev->max_supported <= index) { + dev_err(&edev->dev, "index is larger than max_supported\n"); return -EINVAL; + } state = cable_state ? (1 << index) : 0; return extcon_update_state(edev, 1 << index, state); @@ -592,6 +604,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, return -EINVAL; idx = find_cable_index_by_id(edev, id); + dev_err(&edev->dev, "register nt id:%d(%d)\n", id, idx); spin_lock_irqsave(&edev->lock, flags); ret = raw_notifier_chain_register(&edev->nh[idx], nb); diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c index c893681f3bf3..1913a80ed7bd 100644 --- a/drivers/firmware/qcom/tz_log.c +++ b/drivers/firmware/qcom/tz_log.c @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -942,6 +944,95 @@ err1: g_ion_clnt = NULL; } +static ssize_t proc_qsee_log_func(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) +{ + int len = 0; + memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase, + debug_rw_buf_size); + memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase, + tzdbg.hyp_debug_rw_buf_size); + len = _disp_qsee_log_stats(count); + *ppos = 0; + + if (len > count) + len = count; + + return simple_read_from_buffer(user_buf, len, ppos, tzdbg.stat[6].data, len); +} + + +static const struct file_operations proc_qsee_log_fops = { + .read = proc_qsee_log_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static ssize_t proc_tz_log_func(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) +{ + int len = 0; + memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase, + debug_rw_buf_size); + memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase, + tzdbg.hyp_debug_rw_buf_size); + + if (TZBSP_DIAG_MAJOR_VERSION_LEGACY < + (tzdbg.diag_buf->version >> 16)) { + len = _disp_tz_log_stats(count); + *ppos = 0; + } else { + len = _disp_tz_log_stats_legacy(); + } + + if (len > count) + len = count; + + return simple_read_from_buffer(user_buf, len, ppos, tzdbg.stat[5].data, len); +} + +static const struct file_operations proc_tz_log_fops = { + .read = proc_tz_log_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static int tzprocfs_init(struct platform_device *pdev) +{ + + int rc = 0; + struct proc_dir_entry *prEntry_tmp = NULL; + struct proc_dir_entry *prEntry_dir = NULL; + + prEntry_dir = proc_mkdir("tzdbg", NULL); + + if (prEntry_dir == NULL) { + dev_err(&pdev->dev, "tzdbg procfs_create_dir failed\n"); + return -ENOMEM; + } + + prEntry_tmp = proc_create("qsee_log", 0666, prEntry_dir, &proc_qsee_log_fops); + + if (prEntry_tmp == NULL) { + dev_err(&pdev->dev, "TZ procfs_create_file qsee_log failed\n"); + rc = -ENOMEM; + goto err; + } + + prEntry_tmp = proc_create("tz_log", 0666, prEntry_dir, &proc_tz_log_fops); + + if (prEntry_tmp == NULL) { + dev_err(&pdev->dev, "TZ procfs_create_file tz_log failed\n"); + rc = -ENOMEM; + goto err; + } + + return 0; +err: + proc_remove(prEntry_dir); + + return rc; +} + + static int tzdbgfs_init(struct platform_device *pdev) { int rc = 0; @@ -1155,6 +1246,9 @@ static int tz_log_probe(struct platform_device *pdev) if (tzdbgfs_init(pdev)) goto err; + if (tzprocfs_init(pdev)) + goto err; + tzdbg_register_qsee_log_buf(); tzdbg_get_tz_version(); diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 2557dcda7621..22a344d5961f 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -212,6 +212,8 @@ source "drivers/input/tablet/Kconfig" source "drivers/input/touchscreen/Kconfig" +source "drivers/input/fingerprint/Kconfig" + source "drivers/input/misc/Kconfig" endif diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 2a6d05ab9170..e278536f4827 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_INPUT_FINGERPRINT) += fingerprint/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o diff --git a/drivers/input/fingerprint/Kconfig b/drivers/input/fingerprint/Kconfig new file mode 100644 index 000000000000..36adb1ce512c --- /dev/null +++ b/drivers/input/fingerprint/Kconfig @@ -0,0 +1,24 @@ +menuconfig INPUT_FINGERPRINT + bool "Fingerprint" + default y + help + Say Y here, and a list of supported fingerprint will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_FINGERPRINT + +config FINGERPRINT_DETECT + tristate "fingerprint detect support" + depends on SPI_MASTER + +config FINGERPRINT_FPC + tristate "fpc fingerprint sensor support" + depends on SPI_MASTER + +config FINGERPRINT_GOODIX + tristate "goodix fingerprint sensor support" + depends on SPI_MASTER + +endif diff --git a/drivers/input/fingerprint/Makefile b/drivers/input/fingerprint/Makefile new file mode 100644 index 000000000000..e91a39df7605 --- /dev/null +++ b/drivers/input/fingerprint/Makefile @@ -0,0 +1,4 @@ +#fingerprint_detect should before fpc1022 +obj-$(CONFIG_FINGERPRINT_DETECT) += fingerprint_detect/ +obj-$(CONFIG_FINGERPRINT_FPC) += fpc/ +obj-$(CONFIG_FINGERPRINT_GOODIX) += goodix/ diff --git a/drivers/input/fingerprint/fingerprint_detect/Makefile b/drivers/input/fingerprint/fingerprint_detect/Makefile new file mode 100644 index 000000000000..c9846540e94f --- /dev/null +++ b/drivers/input/fingerprint/fingerprint_detect/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_DETECT) += fingerprint_detect.o \ No newline at end of file diff --git a/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c b/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c new file mode 100644 index 000000000000..088d51fd6b12 --- /dev/null +++ b/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.c @@ -0,0 +1,251 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fingerprint_detect.h" +int fp_version; + +static int fingerprint_detect_request_named_gpio( + struct fingerprint_detect_data *fp_detect, + const char *label, int *gpio) +{ + struct device *dev = fp_detect->dev; + struct device_node *np = dev->of_node; + int rc = of_get_named_gpio(np, label, 0); + + if (rc < 0) { + dev_err(dev, "failed to get '%s'\n", label); + *gpio = rc; + return rc; + } + *gpio = rc; + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + dev_info(dev, "%s - gpio: %d\n", label, *gpio); + return 0; +} + +static ssize_t sensor_version_get(struct device *device, + struct device_attribute *attribute, + char *buffer) +{ + struct fingerprint_detect_data *fp_detect = dev_get_drvdata(device); + + return scnprintf(buffer, PAGE_SIZE, "%i\n", fp_detect->sensor_version); +} + +static DEVICE_ATTR(sensor_version, S_IRUSR, sensor_version_get, NULL); + +static struct attribute *attributes[] = { + &dev_attr_sensor_version.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +int fp_pinctrl_init(struct fingerprint_detect_data *fp_dev) +{ + int ret = 0; + struct device *dev = fp_dev->dev; + + fp_dev->fp_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(fp_dev->fp_pinctrl)) { + dev_err(dev, "Target does not use pinctrl\n"); + ret = PTR_ERR(fp_dev->fp_pinctrl); + goto err; + } + + fp_dev->fp_enable = + pinctrl_lookup_state(fp_dev->fp_pinctrl, "fp_enable"); + if (IS_ERR_OR_NULL(fp_dev->fp_enable)) { + dev_err(dev, "Cannot get fp_enable\n"); + ret = PTR_ERR(fp_dev->fp_enable); + goto err; + } + + ret = pinctrl_select_state(fp_dev->fp_pinctrl, fp_dev->fp_enable); + if (ret) { + dev_err(dev, "can not set %s pins\n", "fp_enable"); + goto err; + } + + fp_dev->id_state_init = + pinctrl_lookup_state(fp_dev->fp_pinctrl, "fp_id_init"); + if (IS_ERR_OR_NULL(fp_dev->id_state_init)) { + dev_err(dev, "Cannot get fp_id_init\n"); + ret = PTR_ERR(fp_dev->id_state_init); + goto err; + } + + ret = pinctrl_select_state(fp_dev->fp_pinctrl, fp_dev->id_state_init); + if (ret) { + dev_err(dev, "can not set %s pins\n", "id_state_init"); + goto err; + } + +err: + fp_dev->fp_pinctrl = NULL; + fp_dev->id_state_init = NULL; + fp_dev->fp_enable = NULL; + return ret; +} + +static int fingerprint_detect_probe(struct platform_device *pdev) +{ + int id0, id1, id2; + int rc = 0; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + struct fingerprint_detect_data *fp_detect = + devm_kzalloc(dev, sizeof(*fp_detect), + GFP_KERNEL); + if (!fp_detect) { + dev_err(dev, + "failed to allocate memory for struct fingerprint_detect_data\n"); + rc = -ENOMEM; + goto exit; + } + + pr_info("%s\n", __func__); + + fp_detect->dev = dev; + dev_set_drvdata(dev, fp_detect); + + if (!np) { + dev_err(dev, "no of node found\n"); + rc = -EINVAL; + goto exit; + } + if (of_property_read_bool(fp_detect->dev->of_node, "oem,dumpling")) + fp_detect->project_version = 0x02; + else + fp_detect->project_version = 0x01; + + rc = fp_pinctrl_init(fp_detect); + if (rc) + goto exit; + + rc = fingerprint_detect_request_named_gpio(fp_detect, "fp-gpio-id0", + &fp_detect->id0_gpio); + if (gpio_is_valid(fp_detect->id0_gpio)) { + dev_err(dev, "%s: gpio_is_valid(fp_detect->id0_gpio=%d)\n", + __func__, fp_detect->id0_gpio); + } + + rc = fingerprint_detect_request_named_gpio(fp_detect, "fp-gpio-id1", + &fp_detect->id1_gpio); + if (gpio_is_valid(fp_detect->id1_gpio)) { + dev_err(dev, "%s: gpio_is_valid(fp_detect->id1_gpio=%d)\n", + __func__, fp_detect->id1_gpio); + } + + rc = fingerprint_detect_request_named_gpio(fp_detect, "fp-gpio-id2", + &fp_detect->id2_gpio); + if (gpio_is_valid(fp_detect->id2_gpio)) { + dev_err(dev, "%s: gpio_is_valid(fp_detect->id2_gpio=%d)\n", + __func__, fp_detect->id2_gpio); + } + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto exit; + } + /** + * ID0(GPIO39) ID1(GPIO41) ID1(GPIO63) + * fpc1245 + * O-film 1 1 1 + * Primax 1 0 0 + * truly 0 0 1 + * + * fpc1263 + * O-film 1 1 0 + * Primax 0 0 0 + * truly 0 1 1 + *fingerchip/ + * qtech 0 1 0 + * Goodix 1 0 1 + */ + id0 = gpio_get_value(fp_detect->id0_gpio); + id1 = gpio_get_value(fp_detect->id1_gpio); + id2 = gpio_get_value(fp_detect->id2_gpio); + pr_info("%s:id %d%d%d\n", __func__, id0, id1, id2); + if (id0 && id1 && id2) { + if (0x02 == fp_detect->project_version) + push_component_info(FINGERPRINTS, "fpc1022", "FPC(OF)"); + else + push_component_info(FINGERPRINTS, "fpc1245", "FPC(OF)"); + fp_detect->sensor_version = 0x01; + } else if (id0 && !id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1245", "FPC(Primax)"); + fp_detect->sensor_version = 0x01; + } else if (!id0 && !id1 && id2) { + push_component_info(FINGERPRINTS, "fpc1245", "FPC(truly)"); + fp_detect->sensor_version = 0x01; + } else if (id0 && id1 && !id2) { + if (0x02 == fp_detect->project_version) { + push_component_info(FINGERPRINTS, "goodix3268", "goodix"); + fp_detect->sensor_version = 0x03; + } else { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(OF)"); + fp_detect->sensor_version = 0x02; + } + } else if (!id0 && !id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(Primax)"); + fp_detect->sensor_version = 0x02; + } else if (!id0 && id1 && id2) { + if (0x02 == fp_detect->project_version) { + push_component_info(FINGERPRINTS, "gfp5288", "Goodix"); + fp_detect->sensor_version = 0x03; + } else { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(truly)"); + fp_detect->sensor_version = 0x02; + } + } else if (!id0 && id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(f/p)"); + fp_detect->sensor_version = 0x02; + } else if (id0 && !id1 && id2) { + push_component_info(FINGERPRINTS, "gfp5288", "Goodix"); + fp_detect->sensor_version = 0x03; + } else { + push_component_info(FINGERPRINTS, "fpc", "PC"); + } + + fp_version = fp_detect->sensor_version; + dev_info(dev, "%s: ok\n", __func__); +exit: + return rc; +} + + +static const struct of_device_id fingerprint_detect_of_match[] = { + { .compatible = "oneplus,fpdetect", }, + {} +}; +MODULE_DEVICE_TABLE(op, fingerprint_detect_of_match); + +static struct platform_driver fingerprint_detect_driver = { + .driver = { + .name = "fingerprint_detect", + .owner = THIS_MODULE, + .of_match_table = fingerprint_detect_of_match, + }, + .probe = fingerprint_detect_probe, +}; +module_platform_driver(fingerprint_detect_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("yale liu"); +MODULE_DESCRIPTION("Fingerprint detect device driver."); diff --git a/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h b/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h new file mode 100644 index 000000000000..5f805fc4f83c --- /dev/null +++ b/drivers/input/fingerprint/fingerprint_detect/fingerprint_detect.h @@ -0,0 +1,17 @@ +#ifndef __FINGERPRINT_DETETC_H_ +#define __FINGERPRINT_DETETC_H_ + +struct fingerprint_detect_data { + struct device *dev; + int id0_gpio; + int id1_gpio; + int id2_gpio; + struct pinctrl *fp_pinctrl; + struct pinctrl_state *id_state_init; + struct pinctrl_state *fp_enable; + int sensor_version; + int project_version; +}; +extern int fp_version; +#endif + diff --git a/drivers/input/fingerprint/fpc/Makefile b/drivers/input/fingerprint/fpc/Makefile new file mode 100644 index 000000000000..43c12d18eb3e --- /dev/null +++ b/drivers/input/fingerprint/fpc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_FPC) += fpc1020_tee.o diff --git a/drivers/input/fingerprint/fpc/fpc1020_tee.c b/drivers/input/fingerprint/fpc/fpc1020_tee.c new file mode 100644 index 000000000000..3c26ef296997 --- /dev/null +++ b/drivers/input/fingerprint/fpc/fpc1020_tee.c @@ -0,0 +1,718 @@ +/* + * FPC1020 Fingerprint sensor device driver + * + * This driver will control the platform resources that the FPC fingerprint + * sensor needs to operate. The major things are probing the sensor to check + * that it is actually connected and let the Kernel know this and with that also + * enabling and disabling of regulators, enabling and disabling of platform + * clocks, controlling GPIOs such as SPI chip select, sensor reset line, sensor + * IRQ line, MISO and MOSI lines. + * + * The driver will expose most of its available functionality in sysfs which + * enables dynamic control of these features from eg. a user space process. + * + * The sensor's IRQ events will be pushed to Kernel's event handling system and + * are exposed in the drivers event node. This makes it possible for a user + * space process to poll the input node and receive IRQ events easily. Usually + * this node is available under /dev/input/eventX where 'X' is a number given by + * the event system. A user space process will need to traverse all the event + * nodes and ask for its parent's name (through EVIOCGNAME) which should match + * the value in device tree named input-device-name. + * + * This driver will NOT send any SPI commands to the sensor it only controls the + * electrical parts. + * + * + * Copyright (c) 2015 Fingerprint Cards AB + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_FB +#include +#include +#endif + +#include +#include "../fingerprint_detect/fingerprint_detect.h" + +static unsigned int ignor_home_for_ESD = 0; +module_param(ignor_home_for_ESD, uint, S_IRUGO | S_IWUSR); + +#define FPC1020_RESET_LOW_US 1000 +#define FPC1020_RESET_HIGH1_US 100 +#define FPC1020_RESET_HIGH2_US 1250 +#define FPC_TTW_HOLD_TIME 1000 + +#define ONEPLUS_EDIT //Onplus modify for msm8996 platform and 15801 HW + +struct fpc1020_data { + struct device *dev; + struct wake_lock ttw_wl; + int irq_gpio; + int rst_gpio; + int irq_num; + struct mutex lock; + bool prepared; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + + #ifdef ONEPLUS_EDIT + int EN_VDD_gpio; + int id0_gpio; + int id1_gpio; + int id2_gpio; + struct input_dev *input_dev; + int screen_state; + /*int sensor_version;*/ + int project_version; + #endif + #if defined(CONFIG_FB) + struct notifier_block fb_notif; + #endif +}; + +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio) +{ + struct device *dev = fpc1020->dev; + struct device_node *np = dev->of_node; + int rc = of_get_named_gpio(np, label, 0); + if (rc < 0) { + dev_err(dev, "failed to get '%s'\n", label); + *gpio = rc; + return rc; + } + *gpio = rc; + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + dev_info(dev, "%s - gpio: %d\n", label, *gpio); + return 0; +} +#ifndef ONEPLUS_EDIT +/* -------------------------------------------------------------------- */ +static int fpc1020_pinctrl_init(struct fpc1020_data *fpc1020) +{ + int ret = 0; + struct device *dev = fpc1020->dev; + + fpc1020->ts_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(fpc1020->ts_pinctrl)) { + dev_err(dev, "Target does not use pinctrl\n"); + ret = PTR_ERR(fpc1020->ts_pinctrl); + goto err; + } + + fpc1020->gpio_state_active = + pinctrl_lookup_state(fpc1020->ts_pinctrl, "pmx_fp_active"); + if (IS_ERR_OR_NULL(fpc1020->gpio_state_active)) { + dev_err(dev, "Cannot get active pinstate\n"); + ret = PTR_ERR(fpc1020->gpio_state_active); + goto err; + } + + fpc1020->gpio_state_suspend = + pinctrl_lookup_state(fpc1020->ts_pinctrl, "pmx_fp_suspend"); + if (IS_ERR_OR_NULL(fpc1020->gpio_state_suspend)) { + dev_err(dev, "Cannot get sleep pinstate\n"); + ret = PTR_ERR(fpc1020->gpio_state_suspend); + goto err; + } + + return 0; +err: + fpc1020->ts_pinctrl = NULL; + fpc1020->gpio_state_active = NULL; + fpc1020->gpio_state_suspend = NULL; + return ret; +} + +/* -------------------------------------------------------------------- */ +static int fpc1020_pinctrl_select(struct fpc1020_data *fpc1020, bool on) +{ + int ret = 0; + struct pinctrl_state *pins_state; + struct device *dev = fpc1020->dev; + + pins_state = on ? fpc1020->gpio_state_active : fpc1020->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + ret = pinctrl_select_state(fpc1020->ts_pinctrl, pins_state); + if (ret) { + dev_err(dev, "can not set %s pins\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + return ret; + } + } else { + dev_err(dev, "not a valid '%s' pinstate\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + } + + return ret; +} +#endif +/* +static int hw_reset(struct fpc1020_data *fpc1020) +{ + int irq_gpio; + struct device *dev = fpc1020->dev; + + int rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + usleep_range(FPC1020_RESET_HIGH1_US, FPC1020_RESET_HIGH1_US + 100); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + if (rc) + goto exit; + usleep_range(FPC1020_RESET_LOW_US, FPC1020_RESET_LOW_US + 100); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + usleep_range(FPC1020_RESET_HIGH1_US, FPC1020_RESET_HIGH1_US + 100); + + irq_gpio = gpio_get_value(fpc1020->irq_gpio); + dev_info(dev, "IRQ after reset %d\n", irq_gpio); +exit: + return rc; +} + +static int hw_reset(struct fpc1020_data *fpc1020) +{ + int irq_gpio; + struct device *dev = fpc1020->dev; + int counter = 2; + + gpio_set_value(fpc1020->EN_VDD_gpio, 0); + mdelay(3); + gpio_set_value(fpc1020->EN_VDD_gpio, 1); + mdelay(3); + //gpio_direction_output(fpc1020->EN_VDD_gpio,1); + + while (counter) { + counter--; + + gpio_set_value(fpc1020->rst_gpio, 1); + udelay(FPC1020_RESET_HIGH1_US); + + gpio_set_value(fpc1020->rst_gpio, 0); + udelay(FPC1020_RESET_LOW_US); + + gpio_set_value(fpc1020->rst_gpio, 1); + udelay(FPC1020_RESET_HIGH2_US); + + irq_gpio = gpio_get_value(fpc1020->irq_gpio); + dev_err(dev, "IRQ after reset %d\n", irq_gpio); + if (irq_gpio) { + //printk(KERN_INFO "%s OK !\n", __func__); + counter = 0; + } else { + dev_err(dev, "%s timed out,retrying ...\n", + __func__); + + udelay(1250); + } + } + return 0; +} +static ssize_t hw_reset_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!strncmp(buf, "reset", strlen("reset"))) + rc = hw_reset(fpc1020); + else + return -EINVAL; + return rc ? rc : count; +} +static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, hw_reset_set); + +*/ +/** + * sysf node to check the interrupt status of the sensor, the interrupt + * handler should perform sysf_notify to allow userland to poll the node. + */ +static ssize_t irq_get(struct device* device, + struct device_attribute* attribute, + char* buffer) +{ + struct fpc1020_data* fpc1020 = dev_get_drvdata(device); + int irq = gpio_get_value(fpc1020->irq_gpio); + return scnprintf(buffer, PAGE_SIZE, "%i\n", irq); +} + + +/** + * writing to the irq node will just drop a printk message + * and return success, used for latency measurement. + */ +static ssize_t irq_ack(struct device* device, + struct device_attribute* attribute, + const char* buffer, size_t count) +{ + struct fpc1020_data* fpc1020 = dev_get_drvdata(device); + dev_dbg(fpc1020->dev, "%s\n", __func__); + return count; +} +static DEVICE_ATTR(irq, S_IRUSR | S_IWUSR, irq_get, irq_ack); + + +static ssize_t report_home_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + //unsigned long time; + + if(ignor_home_for_ESD) + return -EINVAL; + if (!strncmp(buf, "down", strlen("down"))) + { + input_report_key(fpc1020->input_dev, + KEY_HOME, 1); + input_sync(fpc1020->input_dev); + } + else if (!strncmp(buf, "up", strlen("up"))) + { + input_report_key(fpc1020->input_dev, + KEY_HOME, 0); + input_sync(fpc1020->input_dev); + } + else if (!strncmp(buf, "timeout", strlen("timeout"))) + { + input_report_key(fpc1020->input_dev,KEY_F2,1); + input_sync(fpc1020->input_dev); + input_report_key(fpc1020->input_dev,KEY_F2,0); + input_sync(fpc1020->input_dev); + } + else + return -EINVAL; + + return count; +} +static DEVICE_ATTR(report_home, S_IWUSR, NULL, report_home_set); + +static ssize_t report_key_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_event *ev = (struct input_event *)buf; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + input_event(fpc1020->input_dev, ev->type, ev->code, ev->value); + return count; +} +static DEVICE_ATTR(report_key, S_IWUSR, NULL, report_key_set); +static ssize_t update_info_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + //struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!strncmp(buf, "n", strlen("n"))) + { + push_component_info(FINGERPRINTS,"N/A" , "N/A"); + } + + return count; +} +static DEVICE_ATTR(update_info, S_IWUSR, NULL, update_info_set); + +static ssize_t screen_state_get(struct device* device, + struct device_attribute* attribute, + char* buffer) +{ + struct fpc1020_data* fpc1020 = dev_get_drvdata(device); + return scnprintf(buffer, PAGE_SIZE, "%i\n", fpc1020->screen_state); +} + +static DEVICE_ATTR(screen_state, S_IRUSR , screen_state_get, NULL); +/* +static ssize_t sensor_version_get(struct device* device, + struct device_attribute* attribute, + char* buffer) +{ + struct fpc1020_data* fpc1020 = dev_get_drvdata(device); + return scnprintf(buffer, PAGE_SIZE, "%i\n", fpc1020->sensor_version); +} + +static DEVICE_ATTR(sensor_version, S_IRUSR , sensor_version_get, NULL); +*/ +static struct attribute *attributes[] = { + //&dev_attr_hw_reset.attr, + &dev_attr_irq.attr, + &dev_attr_report_home.attr, + &dev_attr_update_info.attr, + &dev_attr_screen_state.attr, + /*&dev_attr_sensor_version.attr,*/ + &dev_attr_report_key.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +int fpc1020_input_init(struct fpc1020_data *fpc1020) +{ + int error = 0; + + + dev_dbg(fpc1020->dev, "%s\n", __func__); + + fpc1020->input_dev = input_allocate_device(); + + if (!fpc1020->input_dev) { + dev_err(fpc1020->dev, "Input_allocate_device failed.\n"); + error = -ENOMEM; + } + + if (!error) { + fpc1020->input_dev->name = "fpc1020"; + + /* Set event bits according to what events we are generating */ + set_bit(EV_KEY, fpc1020->input_dev->evbit); + set_bit(EV_SYN, fpc1020->input_dev->evbit); + set_bit(EV_ABS, fpc1020->input_dev->evbit); + + set_bit(KEY_POWER, fpc1020->input_dev->keybit); + set_bit(KEY_F2, fpc1020->input_dev->keybit); + set_bit(KEY_HOME, fpc1020->input_dev->keybit); + /* + set_bit(BTN_A, fpc1020->input_dev->keybit); + set_bit(BTN_C, fpc1020->input_dev->keybit); + */ + set_bit(BTN_B, fpc1020->input_dev->keybit); + set_bit(ABS_Z, fpc1020->input_dev->keybit); + set_bit(KEY_UP, fpc1020->input_dev->keybit); + set_bit(KEY_DOWN, fpc1020->input_dev->keybit); + set_bit(KEY_LEFT, fpc1020->input_dev->keybit); + set_bit(KEY_RIGHT, fpc1020->input_dev->keybit); + + /* Register the input device */ + error = input_register_device(fpc1020->input_dev); + + + if (error) { + dev_err(fpc1020->dev, "Input_register_device failed.\n"); + input_free_device(fpc1020->input_dev); + fpc1020->input_dev = NULL; + } + } + + return error; +} + +/* -------------------------------------------------------------------- */ +void fpc1020_input_destroy(struct fpc1020_data *fpc1020) +{ + dev_dbg(fpc1020->dev, "%s\n", __func__); + + if (fpc1020->input_dev != NULL) + input_free_device(fpc1020->input_dev); +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + struct fpc1020_data *fpc1020 = container_of(self, struct fpc1020_data, fb_notif); + + if(FB_EARLY_EVENT_BLANK != event && FB_EVENT_BLANK != event) + return 0; + if((evdata) && (evdata->data) && (fpc1020)) { + blank = evdata->data; + if( *blank == FB_BLANK_UNBLANK && (event == FB_EARLY_EVENT_BLANK )) { + dev_err(fpc1020->dev, "%s screen on\n", __func__); + fpc1020->screen_state = 1; + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_screen_state.attr.name); + + } else if( *blank == FB_BLANK_POWERDOWN && (event == FB_EARLY_EVENT_BLANK/*FB_EVENT_BLANK*/ )) { + dev_err(fpc1020->dev, "%s screen off\n", __func__); + fpc1020->screen_state = 0; + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_screen_state.attr.name); + } + } + return 0; +} +#endif + +static irqreturn_t fpc1020_irq_handler(int irq, void *handle) +{ + struct fpc1020_data *fpc1020 = handle; + //dev_err(fpc1020->dev, "%s\n", __func__); + + /* Make sure 'wakeup_enabled' is updated before using it + ** since this is interrupt context (other thread...) */ + //smp_rmb(); +/* + if (fpc1020->wakeup_enabled ) { + wake_lock_timeout(&fpc1020->ttw_wl, msecs_to_jiffies(FPC_TTW_HOLD_TIME)); + } +*/ + wake_lock_timeout(&fpc1020->ttw_wl, msecs_to_jiffies(FPC_TTW_HOLD_TIME));//changhua add for KeyguardUpdateMonitor: fingerprint acquired, grabbing fp wakelock + //dev_err(fpc1020->dev, "%s before sysfs_notify\n", __func__); + + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); + //dev_err(fpc1020->dev, "%s after sysfs_notify\n", __func__); + + return IRQ_HANDLED; +} + +static int fpc1020_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc = 0; + int irqf; + /*int id0, id1, id2;*/ + struct device_node *np; + struct fpc1020_data *fpc1020; + + pr_info("%s: fp version %x\n", __func__, fp_version); + if ((fp_version != 0x01) && (fp_version != 0x02)) + return 0; + np = dev->of_node; + fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020), + GFP_KERNEL); + if (!fpc1020) { + dev_err(dev, + "failed to allocate memory for struct fpc1020_data\n"); + rc = -ENOMEM; + goto exit; + } + + printk(KERN_INFO "%s\n", __func__); + + fpc1020->dev = dev; + dev_set_drvdata(dev, fpc1020); + + if (!np) { + dev_err(dev, "no of node found\n"); + rc = -EINVAL; + goto exit; + } + + if (of_property_read_bool(fpc1020->dev->of_node, "oem,dumpling")) + fpc1020->project_version = 0x02; + else + fpc1020->project_version = 0x01; + + printk(KERN_INFO "%s 111111111111111\n", __func__); + + + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,irq-gpio", + &fpc1020->irq_gpio); + if (rc) + goto exit; + + + printk(KERN_INFO "%s 222222222222222\n", __func__); + + + rc = gpio_direction_input(fpc1020->irq_gpio); + + if (rc) { + dev_err(fpc1020->dev, + "gpio_direction_input (irq) failed.\n"); + goto exit; + } + /* in tz + rc = fpc1020_request_named_gpio(fpc1020, "fpc,reset-gpio", + &fpc1020->rst_gpio); + if (rc) + goto exit;*/ + + #ifdef ONEPLUS_EDIT + /* + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_id0", + &fpc1020->id0_gpio); + if(gpio_is_valid(fpc1020->id0_gpio)) + { + dev_err(dev, "%s: gpio_is_valid(fpc1020->id0_gpio=%d)\n", __func__,fpc1020->id0_gpio); + gpio_direction_input(fpc1020->id0_gpio); + } + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_id1", + &fpc1020->id1_gpio); + if(gpio_is_valid(fpc1020->id1_gpio)) + { + dev_err(dev, "%s: gpio_is_valid(fpc1020->id1_gpio=%d)\n", __func__,fpc1020->id1_gpio); + gpio_direction_input(fpc1020->id1_gpio); + } + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_id2", + &fpc1020->id2_gpio); + if(gpio_is_valid(fpc1020->id2_gpio)) + { + dev_err(dev, "%s: gpio_is_valid(fpc1020->id2_gpio=%d)\n", __func__,fpc1020->id2_gpio); + gpio_direction_input(fpc1020->id2_gpio); + } + */ + /* in xbl + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_1V8_EN", + &fpc1020->EN_VDD_gpio); + if (rc) + goto exit; + gpio_direction_output(fpc1020->EN_VDD_gpio,1);*/ + + #else + rc = fpc1020_pinctrl_init(fpc1020); + if (rc) + goto exit; + + rc = fpc1020_pinctrl_select(fpc1020, true); + if (rc) + goto exit; + + #endif + rc = fpc1020_input_init(fpc1020); + if (rc) + goto exit; + + #if defined(CONFIG_FB) + fpc1020->fb_notif.notifier_call = fb_notifier_callback; + rc = fb_register_client(&fpc1020->fb_notif); + if(rc) + dev_err(fpc1020->dev, "Unable to register fb_notifier: %d\n", rc); + fpc1020->screen_state = 1; + #endif + + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + mutex_init(&fpc1020->lock); + rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio), + NULL, fpc1020_irq_handler, irqf, + dev_name(dev), fpc1020); + if (rc) { + dev_err(dev, "could not request irq %d\n", + gpio_to_irq(fpc1020->irq_gpio)); + goto exit; + } + dev_info(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio)); + + /* Request that the interrupt should not be wakeable */ + //disable_irq_wake( gpio_to_irq( fpc1020->irq_gpio ) ); + + enable_irq_wake( gpio_to_irq( fpc1020->irq_gpio ) ); + wake_lock_init(&fpc1020->ttw_wl, WAKE_LOCK_SUSPEND, "fpc_ttw_wl"); + device_init_wakeup(fpc1020->dev, 1); + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto exit; + } + + #if 0 //changhua remove HW reset here,move to HAL,after spi cs pin become high + rc = gpio_direction_output(fpc1020->rst_gpio, 1); + + if (rc) { + dev_err(fpc1020->dev, + "gpio_direction_output (reset) failed.\n"); + goto exit; + } + + gpio_set_value(fpc1020->rst_gpio, 1); + udelay(FPC1020_RESET_HIGH1_US); + + gpio_set_value(fpc1020->rst_gpio, 0); + udelay(FPC1020_RESET_LOW_US); + + gpio_set_value(fpc1020->rst_gpio, 1); + udelay(FPC1020_RESET_HIGH2_US); + #endif + /** + * ID0(GPIO39) ID1(GPIO41) ID1(GPIO63) + * fpc1245 + * O-film 1 1 1 + * Primax 1 0 0 + * truly 0 0 1 + * + * fpc1263 + * O-film 1 1 0 + * Primax 0 0 0 + * truly 0 1 1 + *fingerchip/ + * qtech 0 1 0 + * Goodix 1 0 1 + * + */ +/* + fpc1020->sensor_version = 0x02; + id0 = gpio_get_value(fpc1020->id0_gpio); + id1 = gpio_get_value(fpc1020->id1_gpio); + id2 = gpio_get_value(fpc1020->id2_gpio); + if (id0 && id1 && id2) { + push_component_info(FINGERPRINTS, "fpc1245", "FPC(OF)"); + fpc1020->sensor_version = 0x01; + } else if (id0 && !id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1245", "FPC(Primax)"); + fpc1020->sensor_version = 0x01; + } else if (!id0 && !id1 && id2) { + push_component_info(FINGERPRINTS, "fpc1245", "FPC(truly)"); + fpc1020->sensor_version = 0x01; + } else if (id0 && id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(OF)"); + } else if (!id0 && !id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(Primax)"); + } else if (!id0 && id1 && id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(truly)"); + } else if (!id0 && id1 && !id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(f/p)"); + } else if (id0 && !id1 && id2) { + push_component_info(FINGERPRINTS, "fpc1263", "FPC(Goodix)"); + } else { + push_component_info(FINGERPRINTS, "fpc", "PC"); + } +*/ + dev_info(dev, "%s: ok\n", __func__); +exit: + return rc; +} + + +static struct of_device_id fpc1020_of_match[] = { + { .compatible = "fpc,fpc1020", }, + {} +}; +MODULE_DEVICE_TABLE(of, fpc1020_of_match); + +static struct platform_driver fpc1020_driver = { + .driver = { + .name = "fpc1020", + .owner = THIS_MODULE, + .of_match_table = fpc1020_of_match, + }, + .probe = fpc1020_probe, +}; +module_platform_driver(fpc1020_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Aleksej Makarov"); +MODULE_AUTHOR("Henrik Tillman "); +MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver."); diff --git a/drivers/input/fingerprint/goodix/Makefile b/drivers/input/fingerprint/goodix/Makefile new file mode 100644 index 000000000000..9faa4a643604 --- /dev/null +++ b/drivers/input/fingerprint/goodix/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_GOODIX) += gf_spi.o platform.o netlink.o diff --git a/drivers/input/fingerprint/goodix/gf_spi.c b/drivers/input/fingerprint/goodix/gf_spi.c new file mode 100644 index 000000000000..0e37312d46ec --- /dev/null +++ b/drivers/input/fingerprint/goodix/gf_spi.c @@ -0,0 +1,915 @@ +/* + * TEE driver for goodix fingerprint sensor + * Copyright (C) 2016 Goodix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gf_spi.h" + +#if defined(USE_SPI_BUS) +#include +#include +#elif defined(USE_PLATFORM_BUS) +#include +#endif + +#include "../fingerprint_detect/fingerprint_detect.h" + +#define VER_MAJOR 1 +#define VER_MINOR 2 +#define PATCH_LEVEL 1 + +#define WAKELOCK_HOLD_TIME 500 /* in ms */ + +#define GF_SPIDEV_NAME "goodix,fingerprint" +/*device name after register in charater*/ +#define GF_DEV_NAME "goodix_fp" +#define GF_INPUT_NAME "gf_input" /*"goodix_fp" */ + +#define CHRD_DRIVER_NAME "goodix_fp_spi" +#define CLASS_NAME "goodix_fp" + +#define N_SPI_MINORS 32 /* ... up to 256 */ +static int SPIDEV_MAJOR; + +static DECLARE_BITMAP(minors, N_SPI_MINORS); +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); +static struct wake_lock fp_wakelock; +static struct gf_dev gf; + +struct gf_key_map maps[] = { + { EV_KEY, GF_KEY_INPUT_HOME }, + { EV_KEY, GF_KEY_INPUT_MENU }, + { EV_KEY, GF_KEY_INPUT_BACK }, + { EV_KEY, GF_KEY_INPUT_POWER }, +#if defined(SUPPORT_NAV_EVENT) + { EV_KEY, GF_NAV_INPUT_UP }, + { EV_KEY, GF_NAV_INPUT_DOWN }, + { EV_KEY, GF_NAV_INPUT_RIGHT }, + { EV_KEY, GF_NAV_INPUT_LEFT }, + { EV_KEY, GF_NAV_INPUT_LONG_PRESS }, + { EV_KEY, GF_NAV_INPUT_F2}, +#endif +}; + +static void gf_enable_irq(struct gf_dev *gf_dev) +{ + if (gf_dev->irq_enabled) { + pr_warn("IRQ has been enabled.\n"); + } else { + enable_irq(gf_dev->irq); + gf_dev->irq_enabled = 1; + } +} + +static void gf_disable_irq(struct gf_dev *gf_dev) +{ + if (gf_dev->irq_enabled) { + gf_dev->irq_enabled = 0; + disable_irq(gf_dev->irq); + } else { + pr_warn("IRQ has been disabled.\n"); + } +} + +#ifdef AP_CONTROL_CLK +static long spi_clk_max_rate(struct clk *clk, unsigned long rate) +{ + long lowest_available, nearest_low, step_size, cur; + long step_direction = -1; + long guess = rate; + int max_steps = 10; + + cur = clk_round_rate(clk, rate); + if (cur == rate) + return rate; + + /* if we got here then: cur > rate */ + lowest_available = clk_round_rate(clk, 0); + if (lowest_available > rate) + return -EINVAL; + + step_size = (rate - lowest_available) >> 1; + nearest_low = lowest_available; + + while (max_steps-- && step_size) { + guess += step_size * step_direction; + cur = clk_round_rate(clk, guess); + + if ((cur < rate) && (cur > nearest_low)) + nearest_low = cur; + /* + * if we stepped too far, then start stepping in the other + * direction with half the step size + */ + if (((cur > rate) && (step_direction > 0)) + || ((cur < rate) && (step_direction < 0))) { + step_direction = -step_direction; + step_size >>= 1; + } + } + return nearest_low; +} + +static void spi_clock_set(struct gf_dev *gf_dev, int speed) +{ + long rate; + int rc; + + rate = spi_clk_max_rate(gf_dev->core_clk, speed); + if (rate < 0) { + pr_info("%s: no match found for requested clock frequency:%d", + __func__, speed); + return; + } + + rc = clk_set_rate(gf_dev->core_clk, rate); +} + +static int gfspi_ioctl_clk_init(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + data->clk_enabled = 0; + data->core_clk = clk_get(&data->spi->dev, "core_clk"); + if (IS_ERR_OR_NULL(data->core_clk)) { + pr_err("%s: fail to get core_clk\n", __func__); + return -EPERM; + } + data->iface_clk = clk_get(&data->spi->dev, "iface_clk"); + if (IS_ERR_OR_NULL(data->iface_clk)) { + pr_err("%s: fail to get iface_clk\n", __func__); + clk_put(data->core_clk); + data->core_clk = NULL; + return -ENOENT; + } + return 0; +} + +static int gfspi_ioctl_clk_enable(struct gf_dev *data) +{ + int err; + + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + return 0; + + err = clk_prepare_enable(data->core_clk); + if (err) { + pr_err("%s: fail to enable core_clk\n", __func__); + return -EPERM; + } + + err = clk_prepare_enable(data->iface_clk); + if (err) { + pr_err("%s: fail to enable iface_clk\n", __func__); + clk_disable_unprepare(data->core_clk); + return -ENOENT; + } + + data->clk_enabled = 1; + + return 0; +} + +static int gfspi_ioctl_clk_disable(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (!data->clk_enabled) + return 0; + + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); + data->clk_enabled = 0; + + return 0; +} + +static int gfspi_ioctl_clk_uninit(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + gfspi_ioctl_clk_disable(data); + + if (!IS_ERR_OR_NULL(data->core_clk)) { + clk_put(data->core_clk); + data->core_clk = NULL; + } + + if (!IS_ERR_OR_NULL(data->iface_clk)) { + clk_put(data->iface_clk); + data->iface_clk = NULL; + } + + return 0; +} +#endif + +static void nav_event_input(struct gf_dev *gf_dev, gf_nav_event_t nav_event) +{ + uint32_t nav_input = 0; + + switch (nav_event) { + case GF_NAV_FINGER_DOWN: + pr_debug("%s nav finger down\n", __func__); + break; + + case GF_NAV_FINGER_UP: + pr_debug("%s nav finger up\n", __func__); + break; + + case GF_NAV_DOWN: + nav_input = GF_NAV_INPUT_DOWN; + pr_debug("%s nav down\n", __func__); + break; + + case GF_NAV_UP: + nav_input = GF_NAV_INPUT_UP; + pr_debug("%s nav up\n", __func__); + break; + + case GF_NAV_LEFT: + nav_input = GF_NAV_INPUT_LEFT; + pr_debug("%s nav left\n", __func__); + break; + + case GF_NAV_RIGHT: + nav_input = GF_NAV_INPUT_RIGHT; + pr_debug("%s nav right\n", __func__); + break; + + case GF_NAV_CLICK: + nav_input = GF_NAV_INPUT_CLICK; + pr_debug("%s nav click\n", __func__); + break; + + case GF_NAV_HEAVY: + nav_input = GF_NAV_INPUT_HEAVY; + pr_debug("%s nav heavy\n", __func__); + break; + + case GF_NAV_LONG_PRESS: + nav_input = GF_NAV_INPUT_LONG_PRESS; + pr_debug("%s nav long press\n", __func__); + break; + + case GF_NAV_DOUBLE_CLICK: + nav_input = GF_NAV_INPUT_DOUBLE_CLICK; + pr_debug("%s nav double click\n", __func__); + break; + case GF_NAV_F2: + nav_input = GF_NAV_INPUT_F2; + pr_debug("%s nav f2\n", __func__); + break; + default: + pr_warn("%s unknown nav event: %d\n", __func__, nav_event); + break; + } + + if ((nav_event != GF_NAV_FINGER_DOWN) && (nav_event != GF_NAV_FINGER_UP)) { + input_report_key(gf_dev->input, nav_input, 1); + input_sync(gf_dev->input); + input_report_key(gf_dev->input, nav_input, 0); + input_sync(gf_dev->input); + } +} + + +static void gf_kernel_key_input(struct gf_dev *gf_dev, struct gf_key *gf_key) +{ + uint32_t key_input = 0; + if (GF_KEY_HOME == gf_key->key) { + key_input = GF_KEY_INPUT_HOME; + } else if (GF_KEY_POWER == gf_key->key) { + key_input = GF_KEY_INPUT_POWER; + } else if (GF_KEY_CAMERA == gf_key->key) { + key_input = GF_KEY_INPUT_CAMERA; + } else { + /* add special key define */ + key_input = gf_key->key; + } + pr_info("%s: received key event[%d], key=%d, value=%d\n", + __func__, key_input, gf_key->key, gf_key->value); + + if ((GF_KEY_POWER == gf_key->key || GF_KEY_CAMERA == gf_key->key) + && (gf_key->value == 1)) { + input_report_key(gf_dev->input, key_input, 1); + input_sync(gf_dev->input); + input_report_key(gf_dev->input, key_input, 0); + input_sync(gf_dev->input); + } + + if (GF_KEY_HOME == gf_key->key) { + input_report_key(gf_dev->input, key_input, gf_key->value); + input_sync(gf_dev->input); + } +} + +static long gf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gf_dev *gf_dev = &gf; + struct gf_key gf_key; +#if defined(SUPPORT_NAV_EVENT) + gf_nav_event_t nav_event = GF_NAV_NONE; +#endif + int retval = 0; + u8 netlink_route = NETLINK_TEST; + struct gf_ioc_chip_info info; + + if (_IOC_TYPE(cmd) != GF_IOC_MAGIC) + return -ENODEV; + + if (_IOC_DIR(cmd) & _IOC_READ) + retval = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + retval = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (retval) + return -EFAULT; + + if (gf_dev->device_available == 0) { + if ((cmd == GF_IOC_ENABLE_POWER) || (cmd == GF_IOC_DISABLE_POWER)) { + pr_info("power cmd\n"); + } else { + pr_info("Sensor is power off currently. \n"); + return -ENODEV; + } + } + + switch (cmd) { + case GF_IOC_INIT: + pr_debug("%s GF_IOC_INIT\n", __func__); + if (copy_to_user((void __user *)arg, (void *)&netlink_route, sizeof(u8))) { + retval = -EFAULT; + break; + } + break; + case GF_IOC_EXIT: + pr_debug("%s GF_IOC_EXIT\n", __func__); + break; + case GF_IOC_DISABLE_IRQ: + pr_debug("%s GF_IOC_DISABEL_IRQ\n", __func__); + gf_disable_irq(gf_dev); + break; + case GF_IOC_ENABLE_IRQ: + pr_debug("%s GF_IOC_ENABLE_IRQ\n", __func__); + gf_enable_irq(gf_dev); + break; + case GF_IOC_RESET: + pr_info("%s GF_IOC_RESET. \n", __func__); + gf_hw_reset(gf_dev, 3); + break; + case GF_IOC_INPUT_KEY_EVENT: + if (copy_from_user(&gf_key, (struct gf_key *)arg, sizeof(struct gf_key))) { + pr_info("Failed to copy input key event from user to kernel\n"); + retval = -EFAULT; + break; + } + + gf_kernel_key_input(gf_dev, &gf_key); + break; +#if defined(SUPPORT_NAV_EVENT) + case GF_IOC_NAV_EVENT: + pr_debug("%s GF_IOC_NAV_EVENT\n", __func__); + if (copy_from_user(&nav_event, (gf_nav_event_t *)arg, sizeof(gf_nav_event_t))) { + pr_info("Failed to copy nav event from user to kernel\n"); + retval = -EFAULT; + break; + } + + nav_event_input(gf_dev, nav_event); + break; +#endif + + case GF_IOC_ENABLE_SPI_CLK: + pr_debug("%s GF_IOC_ENABLE_SPI_CLK\n", __func__); +#ifdef AP_CONTROL_CLK + gfspi_ioctl_clk_enable(gf_dev); +#else + pr_debug("Doesn't support control clock.\n"); +#endif + break; + case GF_IOC_DISABLE_SPI_CLK: + pr_debug("%s GF_IOC_DISABLE_SPI_CLK\n", __func__); +#ifdef AP_CONTROL_CLK + gfspi_ioctl_clk_disable(gf_dev); +#else + pr_debug("Doesn't support control clock\n"); +#endif + break; + case GF_IOC_ENABLE_POWER: + pr_debug("%s GF_IOC_ENABLE_POWER\n", __func__); + if (gf_dev->device_available == 1) + pr_info("Sensor has already powered-on.\n"); + else + gf_power_on(gf_dev); + gf_dev->device_available = 1; + break; + case GF_IOC_DISABLE_POWER: + pr_debug("%s GF_IOC_DISABLE_POWER\n", __func__); + if (gf_dev->device_available == 0) + pr_info("Sensor has already powered-off.\n"); + else + gf_power_off(gf_dev); + gf_dev->device_available = 0; + break; + case GF_IOC_ENTER_SLEEP_MODE: + pr_debug("%s GF_IOC_ENTER_SLEEP_MODE\n", __func__); + break; + case GF_IOC_GET_FW_INFO: + pr_debug("%s GF_IOC_GET_FW_INFO\n", __func__); + break; + case GF_IOC_REMOVE: + pr_debug("%s GF_IOC_REMOVE\n", __func__); + break; + case GF_IOC_CHIP_INFO: + pr_debug("%s GF_IOC_CHIP_INFO\n", __func__); + if (copy_from_user(&info, (struct gf_ioc_chip_info *)arg, sizeof(struct gf_ioc_chip_info))) { + retval = -EFAULT; + break; + } + pr_info("vendor_id : 0x%x\n", info.vendor_id); + pr_info("mode : 0x%x\n", info.mode); + pr_info("operation: 0x%x\n", info.operation); + break; + default: + pr_warn("unsupport cmd:0x%x\n", cmd); + break; + } + + return retval; +} + +#ifdef CONFIG_COMPAT +static long gf_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return gf_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /*CONFIG_COMPAT*/ + +static irqreturn_t gf_irq(int irq, void *handle) +{ +#if defined(GF_NETLINK_ENABLE) + char temp = GF_NET_EVENT_IRQ; + + wake_lock_timeout(&fp_wakelock, msecs_to_jiffies(WAKELOCK_HOLD_TIME)); + sendnlmsg(&temp); +#elif defined GF_FASYNC + struct gf_dev *gf_dev = &gf; + + if (gf_dev->async) + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); +#endif + + return IRQ_HANDLED; +} + +static int gf_open(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(gf_dev, &device_list, device_entry) { + if (gf_dev->devt == inode->i_rdev) { + pr_info("Found\n"); + status = 0; + break; + } + } + + if (status == 0) { + if (status == 0) { + gf_dev->users++; + filp->private_data = gf_dev; + nonseekable_open(inode, filp); + pr_info("Succeed to open device. irq = %d\n", + gf_dev->irq); + if (gf_dev->users == 1) + gf_enable_irq(gf_dev); + gf_hw_reset(gf_dev, 3); + gf_dev->device_available = 1; + } + } else { + pr_info("No device for minor %d\n", iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +#ifdef GF_FASYNC +static int gf_fasync(int fd, struct file *filp, int mode) +{ + struct gf_dev *gf_dev = filp->private_data; + int ret; + + ret = fasync_helper(fd, filp, mode, &gf_dev->async); + pr_info("ret = %d\n", ret); + return ret; +} +#endif + +static int gf_release(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = 0; + + mutex_lock(&device_list_lock); + gf_dev = filp->private_data; + filp->private_data = NULL; + + /*last close?? */ + gf_dev->users--; + if (!gf_dev->users) { + + pr_info("disble_irq. irq = %d\n", gf_dev->irq); + gf_disable_irq(gf_dev); + /*power off the sensor*/ + gf_dev->device_available = 0; + gf_power_off(gf_dev); + } + mutex_unlock(&device_list_lock); + return status; +} + +static const struct file_operations gf_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .unlocked_ioctl = gf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gf_compat_ioctl, +#endif /*CONFIG_COMPAT*/ + .open = gf_open, + .release = gf_release, +#ifdef GF_FASYNC + .fasync = gf_fasync, +#endif +}; + +static int goodix_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct gf_dev *gf_dev; + struct fb_event *evdata = data; + unsigned int blank; + char temp = 0; + + if (val != FB_EARLY_EVENT_BLANK) + return 0; + pr_info("[info] %s go to the goodix_fb_state_chg_callback value = %d\n", + __func__, (int)val); + gf_dev = container_of(nb, struct gf_dev, notifier); + if (evdata && evdata->data && val == FB_EARLY_EVENT_BLANK && gf_dev) { + blank = *(int *)(evdata->data); + switch (blank) { + case FB_BLANK_POWERDOWN: + if (gf_dev->device_available == 1) { + gf_dev->fb_black = 1; +#if defined(GF_NETLINK_ENABLE) + temp = GF_NET_EVENT_FB_BLACK; + sendnlmsg(&temp); +#elif defined (GF_FASYNC) + if (gf_dev->async) { + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); + } +#endif + } + break; + case FB_BLANK_UNBLANK: + if (gf_dev->device_available == 1) { + gf_dev->fb_black = 0; +#if defined(GF_NETLINK_ENABLE) + temp = GF_NET_EVENT_FB_UNBLACK; + sendnlmsg(&temp); +#elif defined (GF_FASYNC) + if (gf_dev->async) { + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); + } +#endif + } + break; + default: + pr_info("%s defalut\n", __func__); + break; + } + } + return NOTIFY_OK; +} + +static struct notifier_block goodix_noti_block = { + .notifier_call = goodix_fb_state_chg_callback, +}; + +static struct class *gf_class; +#if defined(USE_SPI_BUS) +static int gf_probe(struct spi_device *spi) +#elif defined(USE_PLATFORM_BUS) +static int gf_probe(struct platform_device *pdev) +#endif +{ + struct gf_dev *gf_dev = &gf; + int status = -EINVAL; + unsigned long minor; + int i; + + /* Initialize the driver data */ + INIT_LIST_HEAD(&gf_dev->device_entry); +#if defined(USE_SPI_BUS) + gf_dev->spi = spi; +#elif defined(USE_PLATFORM_BUS) + gf_dev->spi = pdev; +#endif + gf_dev->irq_gpio = -EINVAL; + gf_dev->reset_gpio = -EINVAL; + gf_dev->pwr_gpio = -EINVAL; + gf_dev->device_available = 0; + gf_dev->fb_black = 0; + + if (gf_parse_dts(gf_dev)) + goto error_hw; + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + gf_dev->devt = MKDEV(SPIDEV_MAJOR, minor); + dev = device_create(gf_class, &gf_dev->spi->dev, gf_dev->devt, + gf_dev, GF_DEV_NAME); + status = IS_ERR(dev) ? PTR_ERR(dev) : 0; + } else { + dev_dbg(&gf_dev->spi->dev, "no minor number available!\n"); + status = -ENODEV; + mutex_unlock(&device_list_lock); + goto error_hw; + } + + if (status == 0) { + set_bit(minor, minors); + list_add(&gf_dev->device_entry, &device_list); + } else { + gf_dev->devt = 0; + } + mutex_unlock(&device_list_lock); + + if (0x02 == gf_dev->project_version) { + status = gf_pinctrl_init(gf_dev); + if (status) + goto error_hw; + if (get_boot_mode() != MSM_BOOT_MODE__FACTORY) { + status = pinctrl_select_state(gf_dev->gf_pinctrl, + gf_dev->gpio_state_enable); + if (status) { + pr_err("can not set %s pins\n", "fp_en_init"); + goto error_hw; + } + } else { + status = pinctrl_select_state(gf_dev->gf_pinctrl, + gf_dev->gpio_state_disable); + if (status) { + pr_err("can not set %s pins\n", "fp_dis_init"); + goto error_hw; + } + } + } + if (status == 0) { + /*input device subsystem */ + gf_dev->input = input_allocate_device(); + if (gf_dev->input == NULL) { + pr_err("%s, failed to allocate input device\n", __func__); + status = -ENOMEM; + goto error_dev; + } + for (i = 0; i < ARRAY_SIZE(maps); i++) + input_set_capability(gf_dev->input, maps[i].type, maps[i].code); + + gf_dev->input->name = GF_INPUT_NAME; + status = input_register_device(gf_dev->input); + if (status) { + pr_err("failed to register input device\n"); + goto error_input; + } + } +#ifdef AP_CONTROL_CLK + pr_info("Get the clk resource.\n"); + /* Enable spi clock */ + if (gfspi_ioctl_clk_init(gf_dev)) + goto gfspi_probe_clk_init_failed: + + if (gfspi_ioctl_clk_enable(gf_dev)) + goto gfspi_probe_clk_enable_failed; + + spi_clock_set(gf_dev, 1000000); +#endif + + gf_dev->notifier = goodix_noti_block; + fb_register_client(&gf_dev->notifier); + + gf_dev->irq = gf_irq_num(gf_dev); + + wake_lock_init(&fp_wakelock, WAKE_LOCK_SUSPEND, "fp_wakelock"); + status = request_threaded_irq(gf_dev->irq, NULL, gf_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "gf_fp", gf_dev); + + if (status) { + pr_err("failed to request IRQ:%d\n", gf_dev->irq); + goto err_irq; + } + enable_irq_wake(gf_dev->irq); + gf_dev->irq_enabled = 1; + gf_disable_irq(gf_dev); + gpio_set_value(gf_dev->reset_gpio, 0); + + pr_info("version V%d.%d.%02d\n", VER_MAJOR, VER_MINOR, PATCH_LEVEL); + + return status; + +err_irq: + input_unregister_device(gf_dev->input); +#ifdef AP_CONTROL_CLK +gfspi_probe_clk_enable_failed: + gfspi_ioctl_clk_uninit(gf_dev); +gfspi_probe_clk_init_failed: +#endif + +error_input: + if (gf_dev->input != NULL) + input_free_device(gf_dev->input); +error_dev: + if (gf_dev->devt != 0) { + pr_info("Err: status = %d\n", status); + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + mutex_unlock(&device_list_lock); + } +error_hw: + gf_cleanup(gf_dev); + gf_dev->device_available = 0; + + return status; +} + +#if defined(USE_SPI_BUS) +static int gf_remove(struct spi_device *spi) +#elif defined(USE_PLATFORM_BUS) +static int gf_remove(struct platform_device *pdev) +#endif +{ + struct gf_dev *gf_dev = &gf; + + wake_lock_destroy(&fp_wakelock); + /* make sure ops on existing fds can abort cleanly */ + if (gf_dev->irq) + free_irq(gf_dev->irq, gf_dev); + + if (gf_dev->input != NULL) + input_unregister_device(gf_dev->input); + input_free_device(gf_dev->input); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + if (gf_dev->users == 0) + gf_cleanup(gf_dev); + + + fb_unregister_client(&gf_dev->notifier); + mutex_unlock(&device_list_lock); + + return 0; +} + +static struct of_device_id gx_match_table[] = { + { .compatible = GF_SPIDEV_NAME }, + {}, +}; + +#if defined(USE_SPI_BUS) +static struct spi_driver gf_driver = { +#elif defined(USE_PLATFORM_BUS) +static struct platform_driver gf_driver = { +#endif + .driver = { + .name = GF_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = gx_match_table, + }, + .probe = gf_probe, + .remove = gf_remove, +}; + +static int __init gf_init(void) +{ + int status; + + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + pr_info("%s:fp version %x\n", __func__, fp_version); + if(0x03 != fp_version) + return 0; + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(SPIDEV_MAJOR, CHRD_DRIVER_NAME, &gf_fops); + if (status < 0) { + pr_warn("Failed to register char device!\n"); + return status; + } + SPIDEV_MAJOR = status; + gf_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(gf_class)) { + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); + pr_warn("Failed to create class.\n"); + return PTR_ERR(gf_class); + } +#if defined(USE_PLATFORM_BUS) + status = platform_driver_register(&gf_driver); +#elif defined(USE_SPI_BUS) + status = spi_register_driver(&gf_driver); +#endif + if (status < 0) { + class_destroy(gf_class); + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); + pr_warn("Failed to register SPI driver.\n"); + } + +#ifdef GF_NETLINK_ENABLE + netlink_init(); +#endif + pr_info("status = 0x%x\n", status); + return 0; +} +module_init(gf_init); + +static void __exit gf_exit(void) +{ +#ifdef GF_NETLINK_ENABLE + netlink_exit(); +#endif +#if defined(USE_PLATFORM_BUS) + platform_driver_unregister(&gf_driver); +#elif defined(USE_SPI_BUS) + spi_unregister_driver(&gf_driver); +#endif + class_destroy(gf_class); + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); +} +module_exit(gf_exit); + +MODULE_AUTHOR("Jiangtao Yi, "); +MODULE_AUTHOR("Jandy Gou, "); +MODULE_DESCRIPTION("goodix fingerprint sensor device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/fingerprint/goodix/gf_spi.h b/drivers/input/fingerprint/goodix/gf_spi.h new file mode 100644 index 000000000000..df27f5718439 --- /dev/null +++ b/drivers/input/fingerprint/goodix/gf_spi.h @@ -0,0 +1,161 @@ +/* + * driver definition for sensor driver + * + * Coypright (c) 2017 Goodix + */ +#ifndef __GF_SPI_H +#define __GF_SPI_H + +#include +#include +/**********************************************************/ +enum FP_MODE{ + GF_IMAGE_MODE = 0, + GF_KEY_MODE, + GF_SLEEP_MODE, + GF_FF_MODE, + GF_DEBUG_MODE = 0x56 +}; + +#define SUPPORT_NAV_EVENT + +#if defined(SUPPORT_NAV_EVENT) +#define GF_NAV_INPUT_UP KEY_UP +#define GF_NAV_INPUT_DOWN KEY_DOWN +#define GF_NAV_INPUT_LEFT KEY_LEFT +#define GF_NAV_INPUT_RIGHT KEY_RIGHT +#define GF_NAV_INPUT_CLICK KEY_VOLUMEDOWN +#define GF_NAV_INPUT_DOUBLE_CLICK KEY_VOLUMEUP +#define GF_NAV_INPUT_LONG_PRESS BTN_B +#define GF_NAV_INPUT_F2 KEY_F2 +#define GF_NAV_INPUT_HEAVY KEY_CHAT +#endif + +#define GF_KEY_INPUT_HOME KEY_HOME +#define GF_KEY_INPUT_MENU KEY_MENU +#define GF_KEY_INPUT_BACK KEY_BACK +#define GF_KEY_INPUT_POWER KEY_POWER +#define GF_KEY_INPUT_CAMERA KEY_CAMERA + +#if defined(SUPPORT_NAV_EVENT) +typedef enum gf_nav_event { + GF_NAV_NONE = 0, + GF_NAV_FINGER_UP, + GF_NAV_FINGER_DOWN, + GF_NAV_UP, + GF_NAV_DOWN, + GF_NAV_LEFT, + GF_NAV_RIGHT, + GF_NAV_CLICK, + GF_NAV_HEAVY, + GF_NAV_LONG_PRESS, + GF_NAV_DOUBLE_CLICK, + GF_NAV_F2, +} gf_nav_event_t; +#endif + +typedef enum gf_key_event { + GF_KEY_NONE = 0, + GF_KEY_HOME, + GF_KEY_POWER, + GF_KEY_MENU, + GF_KEY_BACK, + GF_KEY_CAMERA, +} gf_key_event_t; + +struct gf_key { + enum gf_key_event key; + uint32_t value; /* key down = 1, key up = 0 */ +}; + +struct gf_key_map { + unsigned int type; + unsigned int code; +}; + +struct gf_ioc_chip_info { + unsigned char vendor_id; + unsigned char mode; + unsigned char operation; + unsigned char reserved[5]; +}; + +#define GF_IOC_MAGIC 'g' //define magic number +#define GF_IOC_INIT _IOR(GF_IOC_MAGIC, 0, uint8_t) +#define GF_IOC_EXIT _IO(GF_IOC_MAGIC, 1) +#define GF_IOC_RESET _IO(GF_IOC_MAGIC, 2) +#define GF_IOC_ENABLE_IRQ _IO(GF_IOC_MAGIC, 3) +#define GF_IOC_DISABLE_IRQ _IO(GF_IOC_MAGIC, 4) +#define GF_IOC_ENABLE_SPI_CLK _IOW(GF_IOC_MAGIC, 5, uint32_t) +#define GF_IOC_DISABLE_SPI_CLK _IO(GF_IOC_MAGIC, 6) +#define GF_IOC_ENABLE_POWER _IO(GF_IOC_MAGIC, 7) +#define GF_IOC_DISABLE_POWER _IO(GF_IOC_MAGIC, 8) +#define GF_IOC_INPUT_KEY_EVENT _IOW(GF_IOC_MAGIC, 9, struct gf_key) +#define GF_IOC_ENTER_SLEEP_MODE _IO(GF_IOC_MAGIC, 10) +#define GF_IOC_GET_FW_INFO _IOR(GF_IOC_MAGIC, 11, uint8_t) +#define GF_IOC_REMOVE _IO(GF_IOC_MAGIC, 12) +#define GF_IOC_CHIP_INFO _IOW(GF_IOC_MAGIC, 13, struct gf_ioc_chip_info) + +#if defined(SUPPORT_NAV_EVENT) +#define GF_IOC_NAV_EVENT _IOW(GF_IOC_MAGIC, 14, gf_nav_event_t) +#define GF_IOC_MAXNR 15 /* THIS MACRO IS NOT USED NOW... */ +#else +#define GF_IOC_MAXNR 14 /* THIS MACRO IS NOT USED NOW... */ +#endif + +//#define AP_CONTROL_CLK 1 +#define USE_PLATFORM_BUS 1 +//#define USE_SPI_BUS 1 +//#define GF_FASYNC 1 /*If support fasync mechanism.*/ +#define GF_NETLINK_ENABLE 1 +#define GF_NET_EVENT_IRQ 1 +#define GF_NET_EVENT_FB_BLACK 2 +#define GF_NET_EVENT_FB_UNBLACK 3 +#define NETLINK_TEST 25 + +struct gf_dev { + dev_t devt; + struct list_head device_entry; +#if defined(USE_SPI_BUS) + struct spi_device *spi; +#elif defined(USE_PLATFORM_BUS) + struct platform_device *spi; +#endif + struct clk *core_clk; + struct clk *iface_clk; + + struct input_dev *input; + /* buffer is NULL unless this device is open (users > 0) */ + unsigned users; + signed irq_gpio; + signed reset_gpio; + signed pwr_gpio; + int irq; + int irq_enabled; + int clk_enabled; +#ifdef GF_FASYNC + struct fasync_struct *async; +#endif + struct notifier_block notifier; + char device_available; + char fb_black; + struct pinctrl *gf_pinctrl; + struct pinctrl_state *gpio_state_enable; + struct pinctrl_state *gpio_state_disable; + signed enable_gpio; + int project_version; +}; +int gf_pinctrl_init(struct gf_dev* gf_dev); +int gf_parse_dts(struct gf_dev* gf_dev); +void gf_cleanup(struct gf_dev *gf_dev); + +int gf_power_on(struct gf_dev *gf_dev); +int gf_power_off(struct gf_dev *gf_dev); + +int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms); +int gf_irq_num(struct gf_dev *gf_dev); + +void sendnlmsg(char *message); +int netlink_init(void); +void netlink_exit(void); +#endif /*__GF_SPI_H*/ diff --git a/drivers/input/fingerprint/goodix/netlink.c b/drivers/input/fingerprint/goodix/netlink.c new file mode 100644 index 000000000000..4053cf81b70b --- /dev/null +++ b/drivers/input/fingerprint/goodix/netlink.c @@ -0,0 +1,98 @@ +/* + * netlink interface + * + * Copyright (c) 2017 Goodix + */ +#include +#include +#include +#include +#include +#include +#include + +#define NETLINK_TEST 25 +#define MAX_MSGSIZE 32 +static int pid = -1; +struct sock *gf_nl_sk = NULL; + +void sendnlmsg(char *message) +{ + struct sk_buff *skb_1; + struct nlmsghdr *nlh; + int len = NLMSG_SPACE(MAX_MSGSIZE); + int slen = 0; + int ret = 0; + if (!message || !gf_nl_sk || !pid) + return ; + + skb_1 = alloc_skb(len, GFP_KERNEL); + if (!skb_1) { + pr_err("alloc_skb error\n"); + return; + } + slen = strlen(message); + nlh = nlmsg_put(skb_1, 0, 0, 0, MAX_MSGSIZE, 0); + + NETLINK_CB(skb_1).portid = 0; + NETLINK_CB(skb_1).dst_group = 0; + + message[slen] = '\0'; + memcpy(NLMSG_DATA(nlh), message, slen+1); + + ret = netlink_unicast(gf_nl_sk, skb_1, pid, MSG_DONTWAIT); + if (!ret) + pr_err("send msg failed ret 0x%x\n", ret); +} + + +void nl_data_ready(struct sk_buff *__skb) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + char str[100]; + skb = skb_get (__skb); + if(skb->len >= NLMSG_SPACE(0)) + { + nlh = nlmsg_hdr(skb); + + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + pid = nlh->nlmsg_pid; + + kfree_skb(skb); + } + +} + + +int netlink_init(void) +{ + struct netlink_kernel_cfg netlink_cfg; + memset(&netlink_cfg, 0, sizeof(struct netlink_kernel_cfg)); + + netlink_cfg.groups = 0; + netlink_cfg.flags = 0; + netlink_cfg.input = nl_data_ready; + netlink_cfg.cb_mutex = NULL; + + gf_nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, + &netlink_cfg); + + if(!gf_nl_sk){ + pr_err("create netlink socket error\n"); + return 1; + } + + return 0; +} + +void netlink_exit(void) +{ + if(gf_nl_sk != NULL){ + netlink_kernel_release(gf_nl_sk); + gf_nl_sk = NULL; + } + + pr_info("self module exited\n"); +} + diff --git a/drivers/input/fingerprint/goodix/platform.c b/drivers/input/fingerprint/goodix/platform.c new file mode 100644 index 000000000000..48e26005967c --- /dev/null +++ b/drivers/input/fingerprint/goodix/platform.c @@ -0,0 +1,165 @@ +/* + * platform indepent driver interface + * + * Coypritht (c) 2017 Goodix + */ +#include +#include +#include +#include +#include +#include +#include + +#include "gf_spi.h" + +#if defined(USE_SPI_BUS) +#include +#include +#elif defined(USE_PLATFORM_BUS) +#include +#endif +int gf_pinctrl_init(struct gf_dev* gf_dev) +{ + int ret = 0; + struct device *dev = &gf_dev->spi->dev; + + gf_dev->gf_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(gf_dev->gf_pinctrl)) { + dev_err(dev, "Target does not use pinctrl\n"); + ret = PTR_ERR(gf_dev->gf_pinctrl); + goto err; + } + + gf_dev->gpio_state_enable = + pinctrl_lookup_state(gf_dev->gf_pinctrl, "fp_en_init"); + if (IS_ERR_OR_NULL(gf_dev->gpio_state_enable)) { + dev_err(dev, "Cannot get active pinstate\n"); + ret = PTR_ERR(gf_dev->gpio_state_enable); + goto err; + } + + gf_dev->gpio_state_disable = + pinctrl_lookup_state(gf_dev->gf_pinctrl, "fp_dis_init"); + if (IS_ERR_OR_NULL(gf_dev->gpio_state_disable)) { + dev_err(dev, "Cannot get active pinstate\n"); + ret = PTR_ERR(gf_dev->gpio_state_disable); + goto err; + } + + return 0; +err: + gf_dev->gf_pinctrl = NULL; + gf_dev->gpio_state_enable = NULL; + gf_dev->gpio_state_disable = NULL; + return ret; +} +int gf_parse_dts(struct gf_dev* gf_dev) +{ + int rc = 0; + + /*get reset resource*/ + gf_dev->reset_gpio = + of_get_named_gpio(gf_dev->spi->dev.of_node, "fp-gpio-reset", 0); + if (!gpio_is_valid(gf_dev->reset_gpio)) { + pr_info("RESET GPIO is invalid.\n"); + rc = -1; + return rc; + } + + rc = gpio_request(gf_dev->reset_gpio, "goodix_reset"); + if (rc) { + dev_err(&gf_dev->spi->dev, "Failed RESET GPIO. rc = %d\n", rc); + return rc; + } + + gpio_direction_output(gf_dev->reset_gpio, 1); + + /*get irq resourece*/ + gf_dev->irq_gpio = + of_get_named_gpio(gf_dev->spi->dev.of_node, "fp-gpio-irq", 0); + pr_info("gf::irq_gpio:%d\n", gf_dev->irq_gpio); + if (!gpio_is_valid(gf_dev->irq_gpio)) { + pr_info("IRQ GPIO is invalid.\n"); + rc = -1; + return rc; + } + + rc = gpio_request(gf_dev->irq_gpio, "goodix_irq"); + if (rc) { + dev_err(&gf_dev->spi->dev, "Failed IRQ GPIO. rc = %d\n", rc); + rc = -1; + return rc; + } + gpio_direction_input(gf_dev->irq_gpio); + if (of_property_read_bool(gf_dev->spi->dev.of_node, "oem,dumpling")) { + gf_dev->project_version = 0x02; + rc = devm_gpio_request(&gf_dev->spi->dev, + gf_dev->enable_gpio, "goodix_en"); + if (rc) { + pr_err("failed to request enable gpio, rc = %d\n", rc); + + } + } + else + gf_dev->project_version = 0x01; + return rc; +} + +void gf_cleanup(struct gf_dev *gf_dev) +{ + pr_info("[info] %s\n",__func__); + if (gpio_is_valid(gf_dev->irq_gpio)) + { + gpio_free(gf_dev->irq_gpio); + pr_info("remove irq_gpio success\n"); + } + if (gpio_is_valid(gf_dev->reset_gpio)) + { + gpio_free(gf_dev->reset_gpio); + pr_info("remove reset_gpio success\n"); + } +} + +int gf_power_on(struct gf_dev* gf_dev) +{ + int rc = 0; + + msleep(10); + pr_info("---- power on ok ----\n"); + + return rc; +} + +int gf_power_off(struct gf_dev* gf_dev) +{ + int rc = 0; + + pr_info("---- power off ----\n"); + return rc; +} + +int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms) +{ + if(gf_dev == NULL) { + pr_info("Input buff is NULL.\n"); + return -1; + } + gpio_direction_output(gf_dev->reset_gpio, 1); + gpio_set_value(gf_dev->reset_gpio, 0); + mdelay(3); + gpio_set_value(gf_dev->reset_gpio, 1); + mdelay(delay_ms); + return 0; +} + +int gf_irq_num(struct gf_dev *gf_dev) +{ + if(gf_dev == NULL) { + pr_info("Input buff is NULL.\n"); + return -1; + } else { + return gpio_to_irq(gf_dev->irq_gpio); + } +} + diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index c93dd193a496..a37ba857fc7a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -358,6 +358,7 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) return; } + if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index dd98d8c8fd1f..3def64389039 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -832,6 +832,13 @@ config INPUT_DRV2667_HAPTICS To compile this driver as a module, choose M here: the module will be called drv2667-haptics. +config TRI_STATE_KEY + default n + tristate "switch Profiles by this triple key" + help + Say Y here if you want to enable the feature. + + source "drivers/input/misc/ots_pat9125/Kconfig" config INPUT_STMVL53L0 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 44c026abcb6f..054acc3ad4eb 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -79,5 +79,6 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_TRI_STATE_KEY) += tri_state_key.o obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/ obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/ diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c index 339f94c072f4..721eeea0d9d6 100644 --- a/drivers/input/misc/qpnp-power-on.c +++ b/drivers/input/misc/qpnp-power-on.c @@ -32,7 +32,16 @@ #include #include #include +#include +#include +#include +#include +#include +#define CREATE_MASK(NUM_BITS, POS) \ + ((unsigned char) (((1 << (NUM_BITS)) - 1) << (POS))) +#define PON_MASK(MSB_BIT, LSB_BIT) \ + CREATE_MASK(MSB_BIT - LSB_BIT + 1, LSB_BIT) #define PMIC_VER_8941 0x01 #define PMIC_VERSION_REG 0x0105 #define PMIC_VERSION_REV4_REG 0x0103 @@ -193,36 +202,9 @@ struct pon_regulator { u32 bit; bool enabled; }; - -struct qpnp_pon { - struct platform_device *pdev; - struct regmap *regmap; - struct input_dev *pon_input; - struct qpnp_pon_config *pon_cfg; - struct pon_regulator *pon_reg_cfg; - struct list_head list; - struct delayed_work bark_work; - struct dentry *debugfs; - int pon_trigger_reason; - int pon_power_off_reason; - int num_pon_reg; - int num_pon_config; - u32 dbc_time_us; - u32 uvlo; - int warm_reset_poff_type; - int hard_reset_poff_type; - int shutdown_poff_type; - u16 base; - u8 subtype; - u8 pon_ver; - u8 warm_reset_reason1; - u8 warm_reset_reason2; - bool is_spon; - bool store_hard_reset_reason; - bool kpdpwr_dbc_enable; - ktime_t kpdpwr_last_release_time; -}; - + struct delayed_work press_work; + struct work_struct up_work; + atomic_t press_count; static int pon_ship_mode_en; module_param_named( ship_mode_en, pon_ship_mode_en, int, 0600 @@ -830,6 +812,18 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) switch (cfg->pon_type) { case PON_KPDPWR: pon_rt_bit = QPNP_PON_KPDPWR_N_SET; + + if ((pon_rt_sts & pon_rt_bit) == 0) + { + printk("Power-Key UP\n"); + schedule_work(&pon->up_work); + cancel_delayed_work(&pon->press_work); + }else{ + printk("Power-Key DOWN\n"); + schedule_delayed_work(&pon->press_work, + msecs_to_jiffies(3000)); + } + break; case PON_RESIN: pon_rt_bit = QPNP_PON_RESIN_N_SET; @@ -857,16 +851,12 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type) * simulate press event in case release event occurred * without a press event */ - if (!cfg->old_state && !key_status) { - input_report_key(pon->pon_input, cfg->key_code, 1); - input_sync(pon->pon_input); - } - input_report_key(pon->pon_input, cfg->key_code, key_status); input_sync(pon->pon_input); cfg->old_state = !!key_status; + return 0; } @@ -1020,6 +1010,87 @@ err_return: return; } +int check_powerkey_count(int press) +{ + int ret=0; + int param_poweroff_count=0; + + ret = get_param_by_index_and_offset(13, 0x30, ¶m_poweroff_count, + sizeof(param_poweroff_count)); + + if(press) + param_poweroff_count ++ ; + else + param_poweroff_count -- ; + + ret = set_param_by_index_and_offset(13, 0x30, ¶m_poweroff_count, + sizeof(param_poweroff_count)); + pr_info("param_poweroff_count=%d \n",param_poweroff_count); + return 0; +} + +int qpnp_powerkey_state_check(struct qpnp_pon *pon,int up) +{ + int rc =0; + + if (get_boot_mode() != MSM_BOOT_MODE__NORMAL) + return 0; + + if ( up ) { + rc = atomic_read(&pon->press_count); + if (rc < 1) { + atomic_inc(&pon->press_count); + check_powerkey_count(1); + } + } + else { + rc = atomic_read(&pon->press_count); + if(rc > 0) { + atomic_dec(&pon->press_count); + check_powerkey_count(0); + } + } + return 0; +} + +static void up_work_func(struct work_struct *work) +{ + struct qpnp_pon *pon = + container_of(work, struct qpnp_pon, up_work); + + qpnp_powerkey_state_check(pon,0); + return; +} + +static void press_work_func(struct work_struct *work) +{ + int rc; + uint pon_rt_sts = 0; + struct qpnp_pon_config *cfg; + struct qpnp_pon *pon = + container_of(work, struct qpnp_pon, press_work.work); + + cfg = qpnp_get_cfg(pon, PON_KPDPWR); + if (!cfg) { + dev_err(&pon->pdev->dev, "Invalid config pointer\n"); + goto err_return; + } + /* check the RT status to get the current status of the line */ + rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read PON RT status\n"); + goto err_return; + } + if ((pon_rt_sts & QPNP_PON_KPDPWR_N_SET) == 1) { + qpnp_powerkey_state_check(pon,1); + dev_err(&pon->pdev->dev, "after 3s Power-Key is still DOWN\n"); + } + msleep(20); + sys_sync(); +err_return: + return; +} + static irqreturn_t qpnp_resin_bark_irq(int irq, void *_pon) { int rc; @@ -1051,6 +1122,93 @@ static irqreturn_t qpnp_resin_bark_irq(int irq, void *_pon) err_exit: return IRQ_HANDLED; } +/*20151106,wujialong add for power dump capture*/ +static int qpnp_config_reset(struct qpnp_pon *pon, struct qpnp_pon_config *cfg); + +static unsigned int pwr_dump_enabled = -1; +static unsigned int long_pwr_dump_enabled = -1; + +static int param_set_pwr_dump_enabled(const char *val, struct kernel_param *kp) +{ + unsigned long enable; + struct qpnp_pon *pon = sys_reset_dev; + struct qpnp_pon_config *cfg = NULL; + int rc; + + if (!val || kstrtoul(val, 0, &enable) || enable > 1) + return -EINVAL; + + cfg = qpnp_get_cfg(pon, 0); /*0 means pwr key */ + if (!cfg) + return -EINVAL; + pr_info("pwr_dump_enabled = %d and request enable = %d\n", + pwr_dump_enabled, (unsigned int)enable); + if (pwr_dump_enabled != enable) { + cfg->s1_timer = 1352; /*reduce this time */ + cfg->s2_type = 1;/*change s2 type to warm reset*/ + rc = qpnp_config_reset(pon, cfg); + + /*if we need enable this feature, */ + /*we should disable wakeup capability */ + if (enable) + disable_irq_wake(cfg->state_irq); + else + enable_irq_wake(cfg->state_irq); + pwr_dump_enabled = enable; + } + return 0; +} + +static int param_set_long_press_pwr_dump_enabled +(const char *val, struct kernel_param *kp) +{ + unsigned long enable; + struct qpnp_pon *pon = sys_reset_dev; + struct qpnp_pon_config *cfg = NULL; + int rc; + u32 s1_timer_bak; + u32 s2_type_bak; + + if (!val || kstrtoul(val, 0, &enable) || enable > 1) + return -EINVAL; + + cfg = qpnp_get_cfg(pon, PON_KPDPWR); /*0 means pwr key*/ + if (!cfg) + return -EINVAL; + + pr_info("long_pwr_dump_enabled = %d enable = %d s1_timer =%d\n", + long_pwr_dump_enabled, + (unsigned int)enable, cfg->s1_timer); + + if (long_pwr_dump_enabled != enable) { + + if (enable) { + s1_timer_bak = cfg->s1_timer; + s2_type_bak = cfg->s2_type; + + cfg->s1_timer = 1352; /*reduce this time */ + cfg->s2_type = 1; /*change s2 type to warm reset*/ + + rc = qpnp_config_reset(pon, cfg); + + cfg->s1_timer = s1_timer_bak; + cfg->s2_type = s2_type_bak; + + } else + rc = qpnp_config_reset(pon, cfg); + long_pwr_dump_enabled = enable; + } + return 0; +} + +module_param_call(pwr_dump_enabled, +param_set_pwr_dump_enabled, param_get_uint, &pwr_dump_enabled, 0644); + +module_param_call(long_pwr_dump_enabled, +param_set_long_press_pwr_dump_enabled, +param_get_uint, &long_pwr_dump_enabled, 0644); + +/*20151106,wujialong add for power dump capture*/ static int qpnp_config_pull(struct qpnp_pon *pon, struct qpnp_pon_config *cfg) @@ -1986,6 +2144,190 @@ static int read_gen2_pon_off_reason(struct qpnp_pon *pon, u16 *reason, return 0; } +static bool created_pwr_on_off_obj; + +#define PMIC_SID_NUM 3 +#define QPNP_PON_POFF_BUFFER_SIZE 128 + +static struct qpnp_pon *g_pon[PMIC_SID_NUM]; +static bool g_is_cold_boot[PMIC_SID_NUM]; + +static ssize_t pwron_reason_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int i; + int index; + u32 pon_sts = 0; + int rc; + char *pbuf = buf; + int ret = 0; + + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, "qpnp_pon_reason :\n"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + + for (i = 0 ; i < ARRAY_SIZE(qpnp_pon_reason) ; i++) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "[%d] : %s\n", i, qpnp_pon_reason[i]); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + + for (i = 0 ; i < PMIC_SID_NUM ; i++) { + /* PON reason */ + if (g_pon[i] == NULL || g_pon[i]->regmap == NULL) + continue; + + /* PON reason */ + rc = regmap_read(g_pon[i]->regmap, + QPNP_PON_REASON1(g_pon[i]), &pon_sts); + if (rc) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "PMIC@SID%d Unable to read PON_RESASON1 reg and rc: %d\n", + to_spmi_device(g_pon[i]->pdev->dev.parent)->usid, + rc); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + continue; + } + + index = ffs(pon_sts) - 1; + cold_boot = !qpnp_pon_is_warm_reset(); + if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "PMIC@SID%d PON_REASON1 regs :[0x%x] and Power-on reason: Unknown and '%s' boot\n", + to_spmi_device(g_pon[i]->pdev->dev.parent)->usid, + pon_sts, + cold_boot ? "cold" : "warm"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + continue; + } else { + g_pon[i]->pon_trigger_reason = index; + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "PMIC@SID%d PON_REASON1 regs :[0x%x] and Power-on reason: '%s' boot and ", + to_spmi_device(g_pon[i]->pdev->dev.parent)->usid, + pon_sts, + cold_boot ? "cold" : "warm"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + + for_each_set_bit(index, (unsigned long *)&pon_sts, + ARRAY_SIZE(qpnp_pon_reason)) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "[%d] ", index); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, "\n"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + + if (ret) + *(buf+ret-1) = '\n'; + + return ret; +} + +static ssize_t pwroff_reason_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int i; + int j; + int index; + int rc; + u8 temp_buf[2]; + u16 poff_sts = 0; + char *pbuf = buf; + int ret = 0; + int reason_index_offset = 0; + + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, "qpnp_poff_reason :\n"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + + for (j = 0; j < ARRAY_SIZE(qpnp_poff_reason); j++) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "[%d] : %s\n", j, qpnp_poff_reason[j]); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + + for (i = 0; i < PMIC_SID_NUM; i++) { + /* POFF reason */ + if (g_pon[i] == NULL || g_pon[i]->regmap == NULL) + continue; + + /* POFF reason */ + if (!is_pon_gen1(g_pon[i]) && g_pon[i]->subtype != PON_1REG) { + rc = read_gen2_pon_off_reason(g_pon[i], &poff_sts, + &reason_index_offset); + if (rc) + return rc; + } else { + rc = regmap_bulk_read(g_pon[i]->regmap, + QPNP_POFF_REASON1(g_pon[i]), + temp_buf, 2); + if (rc) { + dev_err(&g_pon[i]->pdev->dev, "Unable to read POFF_REASON regs rc:%d\n", + rc); + return rc; + } + poff_sts = temp_buf[0] | (temp_buf[1] << 8); + } + index = ffs(poff_sts) - 1 + reason_index_offset; + if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "PMIC@SID%d POFF_REASON regs :[0x%x] and Power-off reason: Unknown\n", + to_spmi_device(g_pon[i]->pdev->dev.parent)->usid, + poff_sts); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + continue; + } else { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "PMIC@SID%d POFF_REASON regs :[0x%x] and Power-off reason: ", + to_spmi_device(g_pon[i]->pdev->dev.parent)->usid, + poff_sts); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + if (index < ARRAY_SIZE(qpnp_poff_reason) && index >= 0) { + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, + "[%d] ", index); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + snprintf(pbuf, QPNP_PON_POFF_BUFFER_SIZE, "\n"); + ret += strlen(pbuf); + pbuf += strlen(pbuf); + } + + if (ret) + *(buf+ret-1) = '\n'; + + return ret; +} + +static struct kobj_attribute pwron_reason_attribute = + __ATTR(pwron_reason, 0444, pwron_reason_show, NULL); +static struct kobj_attribute pwroff_reason_attribute = + __ATTR(pwroff_reason, 0444, pwroff_reason_show, NULL); + +static struct attribute *pwr_on_off_attrs[] = { + &pwron_reason_attribute.attr, + &pwroff_reason_attribute.attr, + NULL, +}; + +static struct attribute_group pwr_on_off_attrs_group = { + .attrs = pwr_on_off_attrs, +}; +static struct kobject *pwr_on_off_kobj; static int qpnp_pon_probe(struct platform_device *pdev) { struct qpnp_pon *pon; @@ -2001,7 +2343,7 @@ static int qpnp_pon_probe(struct platform_device *pdev) u8 s3_src_reg; unsigned long flags; uint temp = 0; - + int i, reg; pon = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_pon), GFP_KERNEL); if (!pon) return -ENOMEM; @@ -2064,6 +2406,17 @@ static int qpnp_pon_probe(struct platform_device *pdev) sizeof(struct qpnp_pon_config) * pon->num_pon_config, GFP_KERNEL); + for (i = 0; i < 16; i++) { + rc = regmap_read(pon->regmap, ((pon)->base + 0xC0+i), ®); + dev_info(&pdev->dev, "(0x%x:0x%x)\n", + ((pon)->base + 0xC0+i), reg); + if (rc) { + dev_err(&pon->pdev->dev, "Unable to read addr=0x%x, rc(%d)\n", + ((pon)->base + 0xC0+i), rc); + return rc; + } + } + /* Read PON_PERPH_SUBTYPE register to get PON type */ rc = regmap_read(pon->regmap, QPNP_PON_PERPH_SUBTYPE(pon), @@ -2128,18 +2481,23 @@ static int qpnp_pon_probe(struct platform_device *pdev) index = ffs(pon_sts) - 1; cold_boot = !qpnp_pon_is_warm_reset(); + for_each_set_bit(index, (unsigned long *)&pon_sts, + ARRAY_SIZE(qpnp_pon_reason)) { if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0) { dev_info(&pon->pdev->dev, - "PMIC@SID%d Power-on reason: Unknown and '%s' boot\n", + "PMIC@SID%d PON_REASON1 regs :[0x%x] and Power-on reason: Unknown and '%s' boot\n", to_spmi_device(pon->pdev->dev.parent)->usid, - cold_boot ? "cold" : "warm"); + pon_sts, + cold_boot ? "cold" : "warm"); } else { pon->pon_trigger_reason = index; dev_info(&pon->pdev->dev, - "PMIC@SID%d Power-on reason: %s and '%s' boot\n", + "PMIC@SID%d PON_REASON1 regs :[0x%x] and Power-on reason: %s and '%s' boot\n", to_spmi_device(pon->pdev->dev.parent)->usid, - qpnp_pon_reason[index], + pon_sts, + qpnp_pon_reason[index], cold_boot ? "cold" : "warm"); + } } /* POFF reason */ @@ -2158,17 +2516,29 @@ static int qpnp_pon_probe(struct platform_device *pdev) } poff_sts = buf[0] | (buf[1] << 8); } + index = ffs(poff_sts) - 1 + reason_index_offset; - if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0) { - dev_info(&pon->pdev->dev, - "PMIC@SID%d: Unknown power-off reason\n", - to_spmi_device(pon->pdev->dev.parent)->usid); - } else { - pon->pon_power_off_reason = index; - dev_info(&pon->pdev->dev, - "PMIC@SID%d: Power-off reason: %s\n", + if (index >= ARRAY_SIZE(qpnp_poff_reason) || index < 0) { + dev_info(&pon->pdev->dev, + "PMIC@SID%d: POFF_REASON regs :[0x%x] and Unknown Power-off reason\n", to_spmi_device(pon->pdev->dev.parent)->usid, + poff_sts); + } else { + pon->pon_power_off_reason = index; + dev_info(&pon->pdev->dev, + "PMIC@SID%d: POFF_REASON regs :[0x%x] and Power-off reason: %s\n", + to_spmi_device(pon->pdev->dev.parent)->usid, + poff_sts, qpnp_poff_reason[index]); + } + + if (to_spmi_device(pon->pdev->dev.parent)->usid >= 0 && + to_spmi_device(pon->pdev->dev.parent)->usid < PMIC_SID_NUM) { + g_pon[to_spmi_device(pon->pdev->dev.parent)->usid] = pon; + g_is_cold_boot[to_spmi_device(pon->pdev->dev.parent)->usid] = + cold_boot; + if (!to_spmi_device(pon->pdev->dev.parent)->usid) + op_pm8998_regmap_register(pon); } if (pon->pon_trigger_reason == PON_SMPL || @@ -2253,7 +2623,8 @@ static int qpnp_pon_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, pon); INIT_DELAYED_WORK(&pon->bark_work, bark_work_func); - + INIT_DELAYED_WORK(&pon->press_work, press_work_func); + INIT_WORK(&pon->up_work, up_work_func); /* register the PON configurations */ rc = qpnp_pon_config_init(pon); if (rc) { @@ -2359,6 +2730,19 @@ static int qpnp_pon_probe(struct platform_device *pdev) pon->store_hard_reset_reason = of_property_read_bool(pdev->dev.of_node, "qcom,store-hard-reset-reason"); + if (!created_pwr_on_off_obj) { + pwr_on_off_kobj = kobject_create_and_add("pwr_on_off_reason", + NULL); + if (!pwr_on_off_kobj) + dev_err(&pdev->dev, "kobject_create_and_add for pwr_on_off_reason failed.\n"); + else if (sysfs_create_group(pwr_on_off_kobj, + &pwr_on_off_attrs_group)) { + dev_err(&pdev->dev, "sysfs_create_group for pwr_on_off_reason failed.\n"); + kobject_put(pwr_on_off_kobj); + } + created_pwr_on_off_obj = true; + } + qpnp_pon_debugfs_init(pdev); return 0; } diff --git a/drivers/input/misc/tri_state_key.c b/drivers/input/misc/tri_state_key.c new file mode 100644 index 000000000000..0929c03a09c9 --- /dev/null +++ b/drivers/input/misc/tri_state_key.c @@ -0,0 +1,386 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#define DRV_NAME "tri-state-key" + +/* + KEY1(GPIO1) KEY2(GPIO92) +pin1 connect to pin4 0 1 | MUTE +pin2 connect to pin5 1 1 | Do Not Disturb +pin4 connect to pin3 1 0 | Normal + +*/ +typedef enum +{ + MODE_UNKNOWN, + MODE_MUTE, + MODE_DO_NOT_DISTURB, + MODE_NORMAL, + MODE_MAX_NUM +} tri_mode_t; + +struct switch_dev_data +{ + int irq_key3; + int irq_key2; + int irq_key1; + int key1_gpio; + int key2_gpio; + int key3_gpio; + + struct regulator *vdd_io; + + struct work_struct work; + struct switch_dev sdev; + struct device *dev; + + struct timer_list s_timer; + struct pinctrl * key_pinctrl; + struct pinctrl_state * set_state; + +}; + +static struct switch_dev_data *switch_data; +static DEFINE_MUTEX(sem); +static int set_gpio_by_pinctrl(void) +{ + return pinctrl_select_state(switch_data->key_pinctrl, switch_data->set_state); +} + +/*set 3 gpio default state high */ +static int key_pre[3] = {1, 1, 1}; +static int delay_time; + +static void switch_dev_work(struct work_struct *work) +{ + int key[3]; + int i, j; + bool have_wrong_key = false; + int state_same = 0; + + msleep(delay_time); + key[0] = gpio_get_value(switch_data->key1_gpio); + key[1] = gpio_get_value(switch_data->key2_gpio); + key[2] = gpio_get_value(switch_data->key3_gpio); + + for (i = 0; i < 3; i++) { + /*if 3 gpio status is the same as before ,ignore them*/ + if (key_pre[i] == key[i]) + state_same++; + if (state_same == 3) + return; + } + + for (i = 0; i < 3; i++) { + /* + * 1,if the gpio key is low ,and previous status is low , + * we suspect that the gpio is in wrong states + */ + if (key[i] + key_pre[i] == 0) { + pr_err("[sk]key[%d] is in wrong state\n", i); + have_wrong_key = true; + delay_time = 300; + break; + } + } + + mutex_lock(&sem); + if (have_wrong_key == true) { + if (key[0]+key[1]+key[2] == 2) { + if (i == 0) + switch_set_state( + &switch_data->sdev, + MODE_MUTE); + if (i == 1) + switch_set_state( + &switch_data->sdev, + MODE_DO_NOT_DISTURB); + if (i == 2) + switch_set_state( + &switch_data->sdev, + MODE_NORMAL); + } + else { + for (j = 0; j < 3; j++) { + /* we got the gpio is wrong state, + * then check which gpio + */ + if ((key[j] == 0) && (i != j)) { + if (j == 0) + switch_set_state( + &switch_data->sdev, + MODE_MUTE); + if (j == 1) + switch_set_state( + &switch_data->sdev, + MODE_DO_NOT_DISTURB); + if (j == 2) + switch_set_state( + &switch_data->sdev, + MODE_NORMAL); + } + } + } + } else { + if (!key[0]) + switch_set_state( + &switch_data->sdev, + MODE_MUTE); + if (!key[1]) + switch_set_state( + &switch_data->sdev, + MODE_DO_NOT_DISTURB); + if (!key[2]) + switch_set_state( + &switch_data->sdev, + MODE_NORMAL); + } + for (i = 0; i < 3; i++) + key_pre[i] = key[i]; + + mutex_unlock(&sem); +} + +static irqreturn_t switch_dev_interrupt(int irq, void *_dev) +{ + schedule_work(&switch_data->work); + return IRQ_HANDLED; +} + +static void timer_handle(unsigned long arg) +{ + schedule_work(&switch_data->work); +} + +#ifdef CONFIG_OF +static int switch_dev_get_devtree_pdata(struct device *dev) +{ + struct device_node *node; + + node = dev->of_node; + if (!node) + return -EINVAL; + + switch_data->key3_gpio= of_get_named_gpio(node, "tristate,gpio_key3", 0); + if ((!gpio_is_valid(switch_data->key3_gpio))) + return -EINVAL; + pr_err("switch_data->key3_gpio=%d \n", switch_data->key3_gpio); + + switch_data->key2_gpio= of_get_named_gpio(node, "tristate,gpio_key2", 0); + if ((!gpio_is_valid(switch_data->key2_gpio))) + return -EINVAL; + pr_err("switch_data->key2_gpio=%d \n", switch_data->key2_gpio); + + switch_data->key1_gpio= of_get_named_gpio(node, "tristate,gpio_key1", 0); + if ((!gpio_is_valid(switch_data->key1_gpio))) + return -EINVAL; + pr_err("switch_data->key1_gpio=%d \n", switch_data->key1_gpio); + + return 0; +} +#else +static inline int +switch_dev_get_devtree_pdata(struct device *dev) +{ + pr_info("%s inline function", __func__); + return 0; +} +#endif + +static int tristate_dev_probe(struct platform_device *pdev) +{ + struct device *dev; + int ret = 0; + + dev= &pdev->dev; + + switch_data = kzalloc(sizeof(struct switch_dev_data), GFP_KERNEL); + if (!switch_data) + return -ENOMEM; + + switch_data->dev = dev; + switch_data->sdev.name = DRV_NAME; + + switch_data->key_pinctrl = devm_pinctrl_get(switch_data->dev); + if (IS_ERR_OR_NULL(switch_data->key_pinctrl)) { + dev_err(switch_data->dev, "Failed to get pinctrl \n"); + goto err_switch_dev_register; + } + switch_data->set_state = pinctrl_lookup_state(switch_data->key_pinctrl, + "pmx_tri_state_key_active"); + if (IS_ERR_OR_NULL(switch_data->set_state)) { + dev_err(switch_data->dev, "Failed to lookup_state \n"); + goto err_switch_dev_register; + } + + set_gpio_by_pinctrl(); + + ret = switch_dev_get_devtree_pdata(dev); + if (ret) { + dev_err(dev, "parse device tree fail!!!\n"); + goto err_switch_dev_register; + } + + ret = switch_dev_register(&switch_data->sdev); + if (ret < 0) + goto err_switch_dev_register; + + //config irq gpio and request irq + ret = gpio_request(switch_data->key1_gpio, "tristate_key1"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->key1_gpio); + if (ret < 0) + goto err_set_gpio_input; + + switch_data->irq_key1 = gpio_to_irq(switch_data->key1_gpio); + if (switch_data->irq_key1 < 0) { + ret = switch_data->irq_key1; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq_key1, switch_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key1", switch_data); + if (ret < 0) + goto err_request_irq; + + ret = gpio_request(switch_data->key2_gpio, + "tristate_key2"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->key2_gpio); + if (ret < 0) + goto err_set_gpio_input; + + switch_data->irq_key2 = gpio_to_irq(switch_data->key2_gpio); + if (switch_data->irq_key2 < 0) { + ret = switch_data->irq_key2; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq_key2, switch_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key2", switch_data); + if (ret < 0) + goto err_request_irq; + + ret = gpio_request(switch_data->key3_gpio, + "tristate_key3"); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->key3_gpio); + if (ret < 0) + goto err_set_gpio_input; + + switch_data->irq_key3 = gpio_to_irq(switch_data->key3_gpio); + if (switch_data->irq_key3 < 0) { + ret = switch_data->irq_key3; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq_key3, switch_dev_interrupt, + IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, + "tristate_key3", switch_data); + if (ret < 0) + goto err_request_irq; + + INIT_WORK(&switch_data->work, switch_dev_work); + + init_timer(&switch_data->s_timer); + switch_data->s_timer.function = &timer_handle; + switch_data->s_timer.expires = jiffies + 5*HZ; + + add_timer(&switch_data->s_timer); + + enable_irq_wake(switch_data->irq_key1); + enable_irq_wake(switch_data->irq_key2); + enable_irq_wake(switch_data->irq_key3); + + return 0; + +err_request_gpio: + switch_dev_unregister(&switch_data->sdev); +err_request_irq: +err_detect_irq_num_failed: +err_set_gpio_input: + gpio_free(switch_data->key2_gpio); + gpio_free(switch_data->key1_gpio); + gpio_free(switch_data->key3_gpio); +err_switch_dev_register: + kfree(switch_data); + + return ret; +} + +static int tristate_dev_remove(struct platform_device *pdev) +{ + cancel_work_sync(&switch_data->work); + gpio_free(switch_data->key1_gpio); + gpio_free(switch_data->key2_gpio); + gpio_free(switch_data->key3_gpio); + switch_dev_unregister(&switch_data->sdev); + kfree(switch_data); + + return 0; +} +#ifdef CONFIG_OF +static struct of_device_id tristate_dev_of_match[] = +{ + { .compatible = "oneplus,tri-state-key", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tristate_dev_of_match); +#endif + +static struct platform_driver tristate_dev_driver = { + .probe = tristate_dev_probe, + .remove = tristate_dev_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tristate_dev_of_match, + }, +}; +static int __init oem_tristate_init(void) +{ + return platform_driver_register(&tristate_dev_driver); +} +module_init(oem_tristate_init); + +static void __exit oem_tristate_exit(void) +{ + platform_driver_unregister(&tristate_dev_driver); +} +module_exit(oem_tristate_exit); +MODULE_DESCRIPTION("oem tri_state_key driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig old mode 100644 new mode 100755 index 2d564aabbc74..037d87a9e092 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1215,6 +1215,25 @@ config TOUCHSCREEN_IT7260_I2C To compile this driver as a module, choose M here: the module will be called it7258_ts_i2c. + +config TOUCHSCREEN_GT9XX + bool "Goodix touchpanel GT9xx series" + depends on I2C + help + Say Y here if you have a Goodix GT9xx touchscreen. + Gt9xx controllers are multi touch controllers which can + report 5 touches at a time. + + If unsure, say N. +config TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI + tristate "Synaptics s3320 i2c touchscreen" + depends on I2C + help + Say Y here if you have a Synaptics s3320 Touchscreen. + + If unsure, say N. + + config TOUCHSCREEN_ST bool "STMicroelectronics Touchscreen Driver" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile old mode 100644 new mode 100755 index f5be6fc19751..2ff3f4f153a7 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -97,4 +97,6 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI) += synaptics_driver_s3320.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_S3320_I2C_RMI) += synaptics_s3320_redremote.o obj-$(CONFIG_TOUCHSCREEN_ST) += st/ diff --git a/drivers/input/touchscreen/fw_update_v7.if b/drivers/input/touchscreen/fw_update_v7.if new file mode 100755 index 000000000000..41cc8d7ee3c3 --- /dev/null +++ b/drivers/input/touchscreen/fw_update_v7.if @@ -0,0 +1,1877 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DO_STARTUP_FW_UPDATE + +#ifdef DO_STARTUP_FW_UPDATE +#ifdef CONFIG_FB +#define WAIT_FOR_FB_READY +#define FB_READY_WAIT_MS 100 +#define FB_READY_TIMEOUT_S 30 +#endif +#endif + +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 3000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 + +#define PRODUCT_ID_SIZE 10 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ENABLE_FLASH_PROG, +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, +}; + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short fl_config; + unsigned short pm_config; + unsigned short bl_config; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short guest_code; + unsigned short pm_config; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_disp_config; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; +struct synaptics_rmi4_fn_desc { + union { + struct { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; + } __packed; + unsigned char data[6]; + }; +}; + + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool force_update; + bool do_lockdown; + bool has_guest_code; + bool new_partition_table; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char flash_status; + unsigned char partitions; + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + unsigned char bl_reset_add; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct workqueue_struct *fwu_workqueue; + struct work_struct fwu_work; + struct i2c_client *client; +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} +/* +static void fwu_compare_partition_tables(void) +{ + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) { + fwu->new_partition_table = true; + return; + } + + if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) { + fwu->new_partition_table = true; + return; + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) { + fwu->new_partition_table = true; + return; + } + } + + fwu->new_partition_table = false; + + return; +} +*/ + +static void fwu_parse_partition_table(const unsigned char *partition_table, + struct block_count *blkcount, struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + //unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + TPD_DEBUG("%s: Partition entry %d:\n", + __func__, ii); +/* + for (offset = 0; offset < 8; offset++) { + TPD_DEBUG("%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } +*/ + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + TPD_DEBUG("%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + TPD_DEBUG("%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + TPD_DEBUG("%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + TPD_DEBUG("%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + TPD_DEBUG("%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + TPD_DEBUG("%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + TPD_DEBUG("%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + TPD_DEBUG("%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + }; + } + + return; +} + + +static void fwu_parse_image_header_10_bl_container(const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10(void) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + TPD_DEBUG("%s ui_firmware_pointer[%p]\n",__func__,fwu->img.ui_firmware.data); + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bl_container(image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } + + return; +} + +static int fwu_parse_image_info(unsigned char *fw) +{ + struct image_header_10 *header; + + header = (struct image_header_10 *)fw; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + fwu_parse_image_header_10(); + + if (!fwu->img.contains_flash_config) { + return -EINVAL; + } + + //fwu_parse_partition_table(fwu->img.fl_config.data, + //&fwu->img.blkcount, &fwu->img.phyaddr); + + //fwu_compare_partition_tables(); + + return 0; +} + +static int fwu_read_flash_status(void) +{ + int retval; + unsigned char status; + unsigned char command; + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + fwu->f34_fd.data_base_addr + fwu->off.flash_status,sizeof(status),&status); + if (retval < 0) { + return retval; + } + //TPD_ERR("flash_status_address[0x%x] status[0x%x]\n",fwu->f34_fd.data_base_addr + fwu->off.flash_status,status); + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else if (fwu->bl_version == BL_V7) + fwu->flash_status = status & MASK_5BIT; + + if (fwu->flash_status != 0x00) { + TPD_ERR("%s: Flash status = %d, command = 0x%02x\n", + __func__, fwu->flash_status, fwu->command); + } + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + sizeof(command),&command); + if (retval < 0) { + TPD_ERR("%s: Failed to read flash command[%d]\n", + __func__,command); + return retval; + } + //TPD_ERR("flash_command_address[0x%x] command[0x%x]\n",fwu->f34_fd.data_base_addr + fwu->off.flash_cmd,command); + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else if (fwu->bl_version == BL_V7) + fwu->command = command; + + return 0; +} + +static int fwu_wait_for_idle(int timeout_ms, bool poll) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + if (poll || (count == timeout_count)) + fwu_read_flash_status(); + + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + } while (count < timeout_count); + + TPD_ERR("%s: Timed out waiting for idle status command[%d],flash_status[%d]\n", + __func__,fwu->command,fwu->flash_status); + + return -ETIMEDOUT; +} +static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd) +{ + int retval; + unsigned char base; + struct f34_v7_data_1_5 data_1_5; + + base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base + fwu->off.partition_id, + sizeof(data_1_5.data),data_1_5.data); + if (retval < 0) { + TPD_ERR("%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.partition_id, + sizeof(data_1_5.data),data_1_5.data); + if (retval < 0) { + TPD_ERR("%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command(unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + TPD_ERR("%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.flash_cmd, + sizeof(command),&command); + if (retval < 0) { + TPD_ERR("%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + + retval = fwu_write_f34_v7_command(cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id(unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char partition; + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + TPD_ERR("%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.partition_id, + sizeof(partition),&partition); + if (retval < 0) { + TPD_ERR("%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id(unsigned char cmd) +{ + int retval; + + retval = fwu_write_f34_v7_partition_id(cmd); + + return retval; +} + +static int fwu_read_f34_v7_partition_table(void) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short block_number = 0; + + base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.block_number, + sizeof(block_number),(unsigned char *)&block_number); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.transfer_length, + sizeof(length), length); + if (retval < 0) { + TPD_ERR("%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(CMD_READ_CONFIG); + if (retval < 0) { + TPD_ERR("%s: Failed to write command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + TPD_ERR("%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base + fwu->off.payload,fwu->partition_table_bytes,fwu->read_config_buf); + if (retval < 0) { + TPD_ERR("%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries(void) +{ + int retval; + unsigned char ii; + unsigned char base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base, + sizeof(query_0.data),query_0.data); + if (retval < 0) { + TPD_ERR("%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base + offset, + sizeof(query_1_7.data),query_1_7.data); + if (retval < 0) { + TPD_ERR("%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + fwu->flash_properties.has_disp_config = query_1_7.has_display_config; + fwu->flash_properties.has_pm_config = query_1_7.has_guest_serialization; + fwu->flash_properties.has_bl_config = query_1_7.has_global_parameters; + + fwu->has_guest_code = query_1_7.has_guest_code; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!fwu->read_config_buf) { + TPD_ERR("%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = fwu->partition_table_bytes; + ptable = fwu->read_config_buf; + + retval = fwu_read_f34_v7_partition_table(); + if (retval < 0) { + TPD_ERR("%s: Failed to read partition table\n", + __func__); + return retval; + } + + fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr); + + return 0; +} + +static int fwu_read_f34_queries(void) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + retval = fwu_read_f34_v7_queries(); + + return retval; +} +static int fwu_write_f34_v7_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + int send_cnt = 1; + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + TPD_ERR("%s: line[%d]Failed to wait for idle status\n",__func__, __LINE__); + } + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.block_number, + sizeof(block_number),(unsigned char *)&block_number); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n",__func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + TPD_DEBUG("send_cnt[%d],transfer_block[%d],remaining[%d]\n",send_cnt++,transfer,remaining); + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.transfer_length, + sizeof(length),length); + if (retval < 0) { + TPD_ERR("%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.payload, + transfer * fwu->block_size,block_ptr); + if (retval < 0) { + TPD_ERR("%s: Failed to write block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + TPD_ERR("%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + block_ptr += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_write_f34_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char cmd) +{ + int retval; + retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd); + return retval; +} + +static int fwu_read_f34_v7_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.block_number, + sizeof(block_number),(unsigned char *)&block_number); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.transfer_length,sizeof(length),length); + if (retval < 0) { + TPD_ERR("%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + TPD_ERR("%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + TPD_ERR("%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base + fwu->off.payload, + transfer * fwu->block_size,&fwu->read_config_buf[index]); + if (retval < 0) { + TPD_ERR("%s: Failed to read block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + retval = fwu_read_f34_v7_blocks(block_cnt, cmd); + + return retval; +} + +static int fwu_check_ui_firmware_size(void) +{ + unsigned short block_count; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + TPD_ERR("%s: UI firmware size mismatch\n",__func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size(void) +{ + unsigned short block_count; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + TPD_ERR("%s: UI configuration size mismatch\n",__func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_bl_configuration_size(void) +{ + unsigned short block_count; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + TPD_ERR("%s: Bootloader configuration size mismatch\n",__func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_write_firmware(void) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_erase_configuration(void) +{ + int retval; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + } + + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + + return retval; +} + +static int fwu_erase_all(void) +{ + int retval; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + TPD_ERR("%s: Erase command written\n",__func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_ERR("%s: Idle status detected\n",__func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(CMD_ERASE_ALL); + if (retval < 0) + return retval; + + TPD_ERR("%s: Erase all command written\n",__func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + TPD_ERR("%s: Idle status detected\n",__func__); + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_configuration(void) +{ + return fwu_write_f34_blocks((unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(void) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +static int fwu_write_flash_configuration(void) +{ + int retval; + + TPD_ERR("%s: enter!!\n",__func__); + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + TPD_ERR("%s: Flash configuration size mismatch\n",__func__); + return -EINVAL; + } + + retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0){ + TPD_ERR("Erase configuration wait Idle status timeout!\n"); + return retval; + } + + retval = fwu_write_configuration(); + if (retval < 0){ + TPD_ERR("write configuration error!\n"); + return retval; + } + TPD_ERR("%s: finish!!\n",__func__); + return 0; +} + +static int fwu_write_partition_table(void) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL); + if (!fwu->read_config_buf) { + TPD_ERR("%s: Failed to alloc mem for fwu->read_config_buf\n",__func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = fwu->config_size; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_do_reflash(void) +{ + int retval; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + return retval; + + retval = fwu_check_bl_configuration_size(); + if (retval < 0) + return retval; + } + + retval = fwu_erase_all(); + if (retval < 0) + return retval; + + if (fwu->new_partition_table) { + retval = fwu_write_partition_table(); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_write_ui_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + return retval; +} + +static int scan_pdt(void) +{ + int retval = 0; + char add_buf[6] = { 0 }; + + retval = synaptics_rmi4_i2c_read_block(fwu->client,0xE9,sizeof(add_buf),add_buf); + if (retval < 0) + return retval; + fwu->f34_fd.query_base_addr = add_buf[0]; + fwu->f34_fd.ctrl_base_addr = add_buf[2]; + fwu->f34_fd.data_base_addr = add_buf[3]; + if (add_buf[4]>>5 && 0x2) + fwu->bl_version = BL_V7; + TPD_ERR("F34_query_base_addr[0x%x],F34_ctrl_base_addr[0x%x],F34_data_base_addr[0x%x]\n",\ + add_buf[0],add_buf[2],add_buf[3]); + + retval = synaptics_rmi4_i2c_read_block(fwu->client,0xE3,sizeof(add_buf),add_buf); + fwu->bl_reset_add = add_buf[1]; + + return retval; +} + +static int fwu_read_f34_serialization(void) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short block_number = 0; + unsigned char buffer[32]; + //static bool judge_version = true; + base = fwu->f34_fd.data_base_addr; + + fwu->config_area = PM_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.block_number, + sizeof(block_number),(unsigned char *)&block_number); + if (retval < 0) { + TPD_ERR("%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->blkcount.pm_config & MASK_8BIT); + length[1] = (unsigned char)(fwu->blkcount.pm_config >> 8); + + retval = synaptics_rmi4_i2c_write_block(fwu->client, + base + fwu->off.transfer_length, + sizeof(length), length); + if (retval < 0) { + TPD_ERR("%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(CMD_READ_CONFIG); + if (retval < 0) { + TPD_ERR("%s: Failed to write command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + TPD_ERR("%s: Failed to wait for idle status\n", + __func__); + return retval; + } + /*buffer = kzalloc(fwu->blkcount.pm_config, GFP_KERNEL); + if (!buffer) { + TPD_ERR("%s: Failed to alloc mem for fwu->blkcount->pm_config\n", + __func__); + return -ENOMEM; + } + */ + retval = synaptics_rmi4_i2c_read_block(fwu->client, + base + fwu->off.payload,fwu->blkcount.pm_config,buffer); + if (retval < 0) { + TPD_ERR("%s: Failed to read block data\n", + __func__); + return retval; + } + TPD_ERR("%s:buffer5[0x%02x]\n",__func__,buffer[5]); + if(!(buffer[0] || buffer[1] || buffer[2] || buffer[4] || buffer[5])){ + TPD_ERR("%s:tp hardware version is 15801\n",__func__); + return 1; + }else{ + TPD_ERR("%s:tp hardware version is 15801vb\n",__func__); + return 0; + } +} + +int fwu_start_reflash_check(const unsigned char *fw_image, struct i2c_client *client) +{ + int retval; + //unsigned char *fw = (unsigned char *)fw_image; + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + TPD_ERR("%s: Failed to alloc mem for fwu\n",__func__); + return -1; + } + + fwu->client = client; + + retval = scan_pdt(); + if (retval < 0) + return retval; + fwu->image = fw_image; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + retval = fwu_read_f34_v7_queries(); + if (retval < 0) { + TPD_ERR("%s: fwu_read_f34_v7_queries\n",__func__); + return retval; + } + TPD_DEBUG("%s: fwu_read_f34_serialization\n",__func__); + + retval = fwu_read_f34_serialization(); + kfree(fwu); + return retval; + } + +int fwu_start_reflash(const unsigned char *fw_image, struct i2c_client *client) +{ + int retval = 0; + unsigned char *fw = (unsigned char *)fw_image; + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + TPD_ERR("%s: Failed to alloc mem for fwu\n",__func__); + return -ENOMEM; + } + fwu->client = client; + + retval = scan_pdt(); + fwu->image = fw_image; + + fwu_read_f34_queries(); + retval = fwu_parse_image_info(fw); + + retval = fwu_read_flash_status(); + if (retval < 0) + goto exit_free_fwu; + + msleep(INT_DISABLE_WAIT_MS); + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto exit_free_fwu; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false); + if (retval < 0){ + goto exit_free_fwu; + } + if (!fwu->in_bl_mode) { + kfree(fwu); + fwu = NULL; + TPD_ERR("%s: BL mode not entered\n",__func__); + return -EINVAL; + } + TPD_ERR("fwu->bl_version[%d] fwu->in_bl_mode[%d]\n",fwu->bl_version,fwu->in_bl_mode); + retval = scan_pdt(); + + TPD_ERR("CMD_ERASE_UI_FIRMWARE.....................\n"); + retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, true); + if (retval < 0){ + TPD_ERR("CMD_ERASE_UI_FIRMWARE timeout!\n"); + goto exit_free_fwu; + } + TPD_ERR("CMD_ERASE_UI_CONFIG.............\n"); + retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG); + retval = fwu_wait_for_idle(ERASE_WAIT_MS, true); + if (retval < 0){ + TPD_ERR("CMD_ERASE_UI_FIRMWARE timeout!\n"); + goto exit_free_fwu; + } + + if (1){ + retval = fwu_write_firmware(); + }else{ + retval = fwu_do_reflash(); + } + retval = fwu_write_ui_configuration(); + synaptics_rmi4_i2c_write_byte(fwu->client,fwu->bl_reset_add,0x01);//reset tp + return retval; + +exit_free_fwu: + kfree(fwu); + fwu = NULL; + return retval; +} diff --git a/drivers/input/touchscreen/synaptics_baseline.h b/drivers/input/touchscreen/synaptics_baseline.h new file mode 100755 index 000000000000..6046f52a44fc --- /dev/null +++ b/drivers/input/touchscreen/synaptics_baseline.h @@ -0,0 +1,657 @@ +#ifndef SYNAPTICS_BASELINE_H +#define SYNAPTICS_BASELINE_H + +#define TX_NUMBER (17) +#define TX_17801_NUMBER (15) +#define RX_NUMBER (30) + +extern bool virtual_key_enable; + +const int16_t baseline_cap_data[2][TX_NUMBER][RX_NUMBER*2] = { + /*enable cbc*/ + { + {403, 748, 441, 819, 451, 837, 451, 837, 450, 836, 459, 852, + 450, 836, 448, 832, 456, 846, 451, 837, 454, 844, 458, 850, + 463, 861, 498, 926, 499, 927, 501, 930, 515, 956, 526, 978, + 529, 982, 528, 980, 531, 987, 544, 1010, 554, 1030, 557, + 1034, 561, 1041, 568, 1056, 568, 1056, 587, 1091, 741, 1375, + 776, 1440}, + {424, 787, 460, 854, 468, 870, 468, 870, 468, 870, 476, 884, + 468, 868, 468, 868, 472, 876, 470, 872, 473, 879, 485, 901, + 485, 901, 522, 969, 522, 969, 522, 969, 531, 987, 534, 992, + 547, 1017, 545, 1013, 557, 1034, 561, 1041, 574, 1066, 592, + 1099, 576, 1070, 592, 1099, 592, 1099, 601, 1115, 774, 1437, + 791, 1469}, + {419, 779, 456, 846, 464, 862, 464, 862, 463, 861, 471, 875, + 464, 862, 462, 858, 470, 872, 466, 865, 470, 872, 473, 879, + 477, 887, 522, 969, 522, 969, 522, 969, 526, 978, 530, 984, + 542, 1006, 540, 1002, 545, 1013, 557, 1034, 566, 1052, 568, + 1056, 571, 1060, 580, 1076, 578, 1074, 592, 1099, 757, 1405, + 783, 1455}, + {420, 780, 456, 846, 466, 865, 466, 865, 464, 862, 473, 879, + 466, 865, 463, 861, 470, 872, 468, 868, 470, 872, 473, 879, + 477, 887, 522, 969, 522, 969, 522, 969, 528, 980, 530, 984, + 543, 1009, 541, 1005, 544, 1010, 566, 1052, 566, 1052, 566, + 1052, 571, 1060, 578, 1074, 578, 1073, 592, 1099, 756, 1404, + 783, 1453}, + {421, 783, 454, 844, 464, 862, 464, 862, 464, 862, 473, 879, + 464, 862, 463, 861, 470, 872, 466, 865, 468, 870, 472, 876, + 476, 884, 512, 952, 510, 948, 522, 969, 526, 978, 529, 982, + 541, 1005, 540, 1002, 552, 1024, 557, 1034, 564, 1048, 565, + 1049, 567, 1053, 575, 1067, 574, 1066, 592, 1099, 756, 1404, + 777, 1443}, + {421, 783, 454, 844, 464, 862, 464, 862, 464, 862, 473, 879, + 464, 862, 463, 861, 470, 872, 468, 868, 468, 870, 473, 879, + 477, 887, 522, 970, 511, 949, 522, 969, 526, 976, 528, 980, + 540, 1002, 539, 1001, 543, 1009, 557, 1034, 565, 1049, 565, + 1049, 567, 1053, 575, 1067, 574, 1066, 582, 1082, 752, 1396, + 774, 1437}, + {418, 776, 453, 841, 464, 862, 466, 865, 463, 861, 472, 876, + 463, 861, 462, 858, 479, 889, 466, 865, 468, 870, 472, 876, + 476, 884, 510, 948, 510, 948, 512, 952, 524, 974, 528, 980, + 540, 1002, 539, 1001, 540, 1002, 552, 1024, 562, 1044, 563, + 1045, 565, 1049, 572, 1062, 572, 1062, 578, 1074, 750, 1394, + 774, 1437}, + {415, 771, 456, 846, 468, 868, 468, 868, 466, 865, 474, 880, + 466, 865, 463, 861, 472, 876, 468, 868, 471, 875, 473, 879, + 477, 887, 511, 949, 511, 949, 522, 969, 526, 976, 530, 984, + 540, 1002, 547, 1017, 540, 1002, 552, 1024, 562, 1044, 562, + 1044, 563, 1045, 574, 1066, 571, 1060, 578, 1073, 752, 1396, + 774, 1437}, + {405, 751, 456, 846, 468, 868, 468, 870, 468, 868, 474, 880, + 468, 868, 466, 865, 473, 879, 479, 889, 472, 876, 475, 883, + 480, 891, 522, 969, 522, 969, 522, 969, 526, 978, 530, 984, + 542, 1006, 539, 1001, 542, 1006, 557, 1034, 565, 1049, 561, + 1041, 563, 1045, 572, 1062, 572, 1062, 575, 1067, 752, 1396, + 774, 1437}, + {415, 771, 460, 854, 472, 876, 472, 876, 471, 875, 480, 891, + 471, 875, 470, 872, 476, 884, 472, 876, 476, 884, 479, 889, + 485, 901, 522, 969, 522, 969, 526, 978, 530, 984, 533, 991, + 544, 1010, 541, 1005, 543, 1009, 557, 1034, 566, 1052, 565, + 1049, 566, 1052, 574, 1066, 574, 1066, 576, 1070, 750, 1394, + 774, 1437}, + {428, 794, 463, 861, 473, 879, 474, 880, 473, 879, 485, 901, + 474, 880, 473, 879, 480, 891, 476, 884, 477, 887, 485, 901, + 485, 901, 522, 969, 522, 969, 522, 969, 532, 988, 534, 992, + 545, 1013, 542, 1006, 544, 1010, 557, 1034, 565, 1049, 565, + 1049, 566, 1052, 574, 1066, 582, 1082, 574, 1066, 760, 1412, + 762, 1416}, + {442, 822, 468, 868, 477, 887, 479, 889, 476, 884, 485, 901, + 477, 887, 475, 883, 485, 901, 477, 887, 491, 913, 485, 901, + 489, 907, 522, 969, 522, 969, 522, 969, 534, 992, 536, 996, + 551, 1023, 543, 1009, 547, 1017, 557, 1035, 584, 1084, 565, + 1049, 566, 1052, 574, 1066, 574, 1066, 570, 1058, 750, 1394, + 762, 1416}, + {450, 836, 470, 872, 479, 889, 479, 889, 476, 884, 487, 904, + 479, 889, 476, 884, 485, 901, 485, 901, 485, 901, 485, 901, + 489, 909, 524, 972, 522, 969, 522, 970, 544, 1010, 536, 996, + 547, 1017, 544, 1010, 547, 1017, 557, 1035, 565, 1049, 565, + 1049, 567, 1053, 574, 1066, 574, 1066, 568, 1056, 753, 1398, + 774, 1437}, + {454, 844, 472, 876, 485, 901, 485, 901, 480, 891, 487, 905, + 485, 901, 479, 889, 487, 904, 485, 901, 485, 901, 487, 905, + 491, 913, 524, 974, 522, 970, 522, 970, 536, 995, 536, 996, + 549, 1019, 544, 1010, 547, 1017, 559, 1037, 567, 1053, 566, + 1052, 578, 1073, 575, 1067, 575, 1067, 574, 1066, 756, 1404, + 774, 1437}, + {463, 861, 476, 884, 485, 901, 487, 905, 485, 901, 489, 909, + 485, 901, 485, 901, 489, 907, 485, 901, 487, 904, 494, 918, + 494, 918, 528, 980, 526, 976, 526, 978, 539, 1001, 540, 1002, + 557, 1034, 547, 1017, 551, 1023, 562, 1044, 574, 1066, 574, + 1066, 574, 1066, 582, 1082, 584, 1084, 595, 1105, 762, 1414, + 791, 1469}, + {498, 926, 498, 926, 498, 926, 498, 926, 494, 918, 498, 926, + 494, 918, 498, 926, 494, 918, 489, 909, 489, 909, 494, 918, + 498, 926, 534, 992, 534, 992, 530, 984, 543, 1009, 543, 1009, + 570, 1058, 547, 1017, 552, 1024, 561, 1041, 589, 1095, 570, + 1058, 574, 1066, 582, 1082, 582, 1082, 596, 1106, 769, 1429, + 800, 1486}, + {498, 926, 498, 926, 498, 926, 498, 926, 494, 918, 498, 926, + 494, 918, 498, 926, 494, 918, 489, 909, 489, 909, 494, 918, + 498, 926, 534, 992, 534, 992, 530, 984, 543, 1009, 543, 1009, + 570, 1058, 547, 1017, 552, 1024, 561, 1041, 589, 1095, 570, + 1058, 574, 1066, 582, 1082, 582, 1082, 596, 1106, 500, 3000, + 500, 3000}, + }, + /*disable cbc*/ + { + {608, 1130, 650, 1208, 655, 1216, 652, 1210, 652, 1212, 658, + 1222, 652, 1212, 655, 1216, 650, 1208, 655, 1216, 656, 1218, + 668, 1240, 677, 1257, 673, 1249, 681, 1265, 691, 1283, 685, + 1273, 713, 1325, 713, 1325, 723, 1343, 725, 1347, 731, 1357, + 734, 1362, 739, 1372, 734, 1362, 747, 1387, 741, 1375, 776, + 1440, 741, 1375, 776, 1440}, + {633, 1175, 670, 1244, 677, 1257, 671, 1247, 673, 1249, 678, + 1258, 673, 1251, 677, 1257, 668, 1240, 677, 1257, 677, 1257, + 704, 1307, 704, 1307, 704, 1307, 704, 1307, 712, 1322, 706, + 1310, 723, 1343, 739, 1372, 743, 1379, 747, 1387, 756, 1404, + 756, 1404, 774, 1437, 756, 1404, 774, 1437, 774, 1437, 791, + 1469, 774, 1437, 791, 1469}, + {633, 1175, 668, 1240, 673, 1249, 669, 1243, 670, 1244, 675, + 1253, 671, 1247, 673, 1251, 668, 1240, 673, 1249, 673, 1251, + 685, 1273, 704, 1307, 704, 1307, 710, 1318, 708, 1316, 704, + 1307, 721, 1339, 731, 1357, 741, 1375, 743, 1379, 747, 1387, + 750, 1394, 756, 1404, 749, 1391, 762, 1416, 757, 1405, 783, + 1455, 757, 1405, 783, 1455}, + {633, 1175, 668, 1240, 673, 1249, 669, 1243, 671, 1247, 678, + 1258, 673, 1249, 675, 1253, 668, 1240, 673, 1251, 676, 1255, + 688, 1278, 704, 1307, 704, 1307, 704, 1307, 710, 1318, 704, + 1307, 722, 1340, 732, 1359, 741, 1375, 743, 1379, 757, 1405, + 752, 1396, 753, 1398, 748, 1390, 762, 1414, 756, 1404, 783, + 1453, 756, 1404, 783, 1453}, + {633, 1175, 668, 1240, 670, 1244, 668, 1240, 670, 1244, 675, + 1253, 673, 1249, 673, 1251, 668, 1240, 671, 1247, 675, 1253, + 687, 1275, 704, 1307, 704, 1307, 704, 1307, 708, 1314, 704, + 1307, 721, 1339, 729, 1355, 739, 1372, 752, 1396, 745, 1383, + 748, 1390, 752, 1396, 745, 1383, 760, 1411, 756, 1404, 777, + 1443, 756, 1404, 777, 1443}, + {633, 1175, 664, 1232, 669, 1243, 668, 1240, 668, 1240, 673, + 1251, 670, 1244, 671, 1247, 668, 1240, 670, 1244, 673, 1249, + 685, 1273, 704, 1307, 704, 1307, 704, 1307, 706, 1310, 704, + 1307, 718, 1333, 727, 1351, 739, 1372, 739, 1372, 743, 1379, + 747, 1387, 748, 1390, 743, 1379, 756, 1404, 752, 1396, 774, + 1437, 752, 1396, 774, 1437}, + {621, 1153, 662, 1229, 668, 1240, 668, 1240, 668, 1240, 673, + 1251, 670, 1244, 671, 1247, 675, 1253, 670, 1244, 673, 1249, + 685, 1273, 704, 1307, 704, 1307, 704, 1307, 706, 1310, 704, + 1307, 718, 1333, 727, 1351, 739, 1372, 739, 1372, 743, 1379, + 747, 1387, 748, 1390, 743, 1379, 757, 1405, 750, 1394, 774, + 1437, 750, 1394, 774, 1437}, + {620, 1151, 668, 1240, 671, 1247, 668, 1240, 670, 1244, 677, + 1257, 673, 1249, 673, 1251, 668, 1240, 673, 1251, 675, 1253, + 688, 1278, 704, 1307, 704, 1307, 704, 1307, 708, 1314, 704, + 1307, 721, 1339, 729, 1355, 747, 1387, 739, 1372, 743, 1379, + 747, 1387, 750, 1394, 744, 1382, 757, 1405, 752, 1396, 774, + 1437, 752, 1396, 774, 1437}, + {621, 1153, 668, 1240, 675, 1253, 670, 1244, 673, 1251, 678, + 1258, 673, 1251, 677, 1257, 669, 1243, 685, 1273, 678, 1258, + 704, 1307, 704, 1307, 704, 1307, 704, 1307, 710, 1318, 704, + 1308, 721, 1339, 731, 1357, 739, 1372, 739, 1373, 743, 1379, + 749, 1391, 750, 1394, 744, 1382, 757, 1405, 752, 1396, 774, + 1437, 752, 1396, 774, 1437}, + {633, 1175, 668, 1240, 676, 1255, 673, 1251, 676, 1255, 681, + 1265, 677, 1257, 679, 1261, 673, 1249, 677, 1257, 679, 1261, + 704, 1307, 704, 1307, 704, 1307, 704, 1308, 723, 1343, 706, + 1312, 723, 1343, 732, 1359, 739, 1373, 741, 1375, 745, 1383, + 750, 1394, 752, 1396, 744, 1382, 756, 1404, 750, 1394, 774, + 1437, 750, 1394, 774, 1437}, + {639, 1187, 671, 1247, 678, 1258, 675, 1253, 677, 1257, 683, + 1268, 679, 1261, 680, 1264, 673, 1251, 679, 1261, 681, 1265, + 704, 1307, 704, 1307, 704, 1307, 706, 1310, 713, 1325, 708, + 1314, 723, 1343, 732, 1359, 741, 1375, 741, 1377, 745, 1383, + 747, 1387, 749, 1391, 743, 1379, 756, 1404, 760, 1412, 762, + 1416, 760, 1412, 762, 1416}, + {647, 1201, 675, 1253, 679, 1261, 677, 1257, 679, 1261, 685, + 1273, 681, 1265, 683, 1268, 677, 1257, 681, 1265, 704, 1307, + 704, 1307, 704, 1307, 704, 1307, 708, 1314, 716, 1330, 710, + 1318, 725, 1347, 739, 1372, 741, 1377, 743, 1379, 747, 1387, + 774, 1437, 750, 1394, 744, 1382, 756, 1404, 750, 1394, 762, + 1416, 750, 1394, 762, 1416}, + {655, 1216, 678, 1258, 683, 1269, 680, 1264, 681, 1265, 688, + 1278, 683, 1268, 685, 1273, 679, 1261, 685, 1273, 685, 1273, + 704, 1307, 706, 1310, 704, 1307, 710, 1318, 718, 1333, 722, + 1340, 727, 1351, 739, 1372, 744, 1382, 744, 1382, 748, 1390, + 750, 1394, 752, 1396, 746, 1386, 758, 1408, 753, 1398, 774, + 1437, 753, 1398, 774, 1437}, + {660, 1226, 681, 1265, 685, 1273, 681, 1265, 683, 1268, 704, + 1307, 685, 1273, 687, 1275, 680, 1264, 685, 1273, 688, 1278, + 704, 1307, 706, 1312, 704, 1307, 710, 1318, 718, 1333, 712, + 1322, 727, 1351, 739, 1372, 744, 1382, 744, 1382, 748, 1390, + 752, 1396, 752, 1396, 756, 1404, 760, 1411, 756, 1404, 774, + 1437, 756, 1404, 774, 1437}, + {668, 1240, 685, 1273, 688, 1278, 704, 1307, 687, 1275, 704, + 1307, 687, 1275, 704, 1307, 681, 1265, 688, 1278, 704, 1307, + 706, 1312, 711, 1320, 706, 1312, 714, 1326, 721, 1339, 714, + 1326, 732, 1359, 741, 1377, 748, 1390, 747, 1387, 753, 1398, + 757, 1405, 758, 1408, 753, 1398, 774, 1437, 762, 1414, 791, + 1469, 762, 1414, 791, 1469}, + {716, 1330, 716, 1330, 716, 1330, 701, 1301, 701, 1301, 712, + 1322, 699, 1297, 716, 1330, 699, 1297, 699, 1297, 699, 1297, + 716, 1330, 725, 1347, 721, 1339, 734, 1362, 734, 1362, 725, + 1347, 734, 1362, 769, 1429, 760, 1412, 760, 1412, 764, 1420, + 787, 1461, 764, 1420, 764, 1420, 769, 1429, 769, 1429, 800, + 1486, 769, 1429, 800, 1486}, + {716, 1330, 716, 1330, 716, 1330, 701, 1301, 701, 1301, 712, + 1322, 699, 1297, 716, 1330, 699, 1297, 699, 1297, 699, 1297, + 716, 1330, 725, 1347, 721, 1339, 734, 1362, 734, 1362, 725, + 1347, 734, 1362, 769, 1429, 760, 1412, 760, 1412, 764, 1420, + 787, 1461, 764, 1420, 764, 1420, 769, 1429, 769, 1429, 800, + 1486, 500, 3000, 500, 3000}, + } + +}; + +const int16_t baseline_cap_17801_data[2][TX_17801_NUMBER][RX_NUMBER*2] = { +/*enable cbc*/ + { + {662, 1544, 509, 1188, 510, 1189, 510, 1191, 510, + 1189, 511, 1192, 510, 1190, 511, 1192, 512, 1195, + 512, 1194, 514, 1199, 514, 1199, 515, 1202, 518, + 1209, 517, 1207, 521, 1215, 523, 1219, 525, 1225, + 526, 1228, 535, 1249, 528, 1233, 532, 1240, 533, + 1244, 533, 1244, 535, 1249, 538, 1255, 537, 1252, + 537, 1253, 539, 1259, 740, 1727}, + {560, 1307, 592, 1381, 593, 1384, 594, 1387, 594, + 1385, 595, 1388, 594, 1386, 595, 1387, 596, 1391, + 595, 1389, 598, 1395, 597, 1394, 599, 1398, 602, + 1405, 601, 1402, 605, 1411, 614, 1433, 609, 1421, + 610, 1424, 612, 1427, 612, 1428, 616, 1436, 617, + 1440, 617, 1441, 620, 1446, 622, 1451, 621, 1449, + 621, 1449, 623, 1453, 625, 1459}, + {562, 1311, 593, 1383, 595, 1387, 596, 1390, 595, + 1388, 596, 1391, 595, 1389, 596, 1391, 598, 1394, + 597, 1392, 599, 1398, 599, 1397, 600, 1400, 603, + 1407, 602, 1405, 613, 1430, 607, 1417, 610, 1422, + 611, 1425, 612, 1429, 613, 1429, 616, 1437, 618, + 1441, 618, 1441, 620, 1446, 622, 1451, 621, 1449, + 621, 1449, 622, 1452, 622, 1451}, + {563, 1313, 592, 1381, 594, 1386, 595, 1389, 594, + 1387, 596, 1390, 595, 1388, 595, 1389, 597, 1393, + 596, 1391, 599, 1397, 598, 1396, 600, 1399, 603, + 1406, 601, 1403, 604, 1410, 606, 1414, 609, 1421, + 610, 1424, 611, 1427, 612, 1427, 615, 1435, 617, + 1439, 624, 1457, 618, 1443, 620, 1448, 619, 1445, + 619, 1445, 621, 1448, 619, 1444}, + {563, 1314, 591, 1379, 593, 1384, 594, 1387, 594, + 1386, 595, 1389, 594, 1386, 595, 1388, 596, 1392, + 596, 1390, 598, 1395, 597, 1394, 599, 1398, 602, + 1404, 601, 1401, 604, 1408, 605, 1413, 608, 1418, + 617, 1440, 611, 1425, 611, 1425, 614, 1432, 615, + 1436, 615, 1436, 617, 1440, 619, 1445, 618, 1442, + 618, 1441, 619, 1444, 616, 1437}, + {564, 1316, 591, 1378, 593, 1383, 594, 1386, 594, + 1385, 595, 1388, 594, 1386, 594, 1387, 596, 1391, + 595, 1389, 597, 1394, 597, 1393, 599, 1397, 601, + 1403, 600, 1401, 603, 1408, 605, 1412, 608, 1418, + 609, 1420, 610, 1423, 610, 1423, 613, 1431, 615, + 1434, 615, 1434, 624, 1456, 618, 1443, 617, 1440, + 617, 1439, 618, 1442, 613, 1431}, + {565, 1319, 591, 1379, 593, 1384, 594, 1387, 594, + 1385, 595, 1388, 594, 1386, 594, 1387, 596, 1390, + 595, 1388, 597, 1394, 597, 1393, 598, 1396, 601, + 1403, 600, 1400, 603, 1408, 605, 1412, 607, 1417, + 608, 1420, 610, 1422, 617, 1441, 613, 1430, 614, + 1433, 614, 1433, 616, 1437, 618, 1442, 617, 1439, + 616, 1438, 617, 1440, 611, 1426}, + {568, 1324, 591, 1380, 593, 1385, 595, 1388, 594, + 1386, 595, 1389, 594, 1386, 595, 1388, 596, 1391, + 595, 1389, 598, 1394, 597, 1393, 599, 1397, 601, + 1403, 600, 1401, 603, 1408, 605, 1412, 615, 1436, + 609, 1420, 610, 1422, 610, 1422, 613, 1429, 614, + 1432, 614, 1432, 616, 1436, 618, 1441, 616, 1438, + 616, 1437, 617, 1439, 610, 1423}, + {570, 1330, 592, 1382, 594, 1386, 595, 1389, 595, + 1388, 596, 1390, 595, 1388, 595, 1389, 597, 1393, + 596, 1391, 598, 1396, 598, 1395, 599, 1399, 602, + 1405, 609, 1420, 604, 1409, 605, 1413, 608, 1418, + 609, 1420, 610, 1423, 610, 1423, 613, 1429, 614, + 1432, 614, 1432, 615, 1436, 617, 1440, 616, 1437, + 616, 1437, 617, 1439, 610, 1424}, + {572, 1335, 593, 1383, 595, 1388, 596, 1391, 595, + 1389, 596, 1392, 596, 1390, 596, 1391, 598, 1395, + 597, 1393, 599, 1398, 599, 1397, 600, 1400, 603, + 1406, 601, 1403, 604, 1410, 606, 1414, 608, 1419, + 609, 1421, 610, 1424, 610, 1424, 613, 1430, 614, + 1433, 614, 1432, 616, 1436, 617, 1440, 616, 1437, + 616, 1437, 624, 1457, 607, 1417}, + {576, 1345, 595, 1387, 596, 1392, 598, 1395, 597, + 1393, 598, 1396, 598, 1394, 598, 1396, 600, 1399, + 599, 1397, 601, 1402, 601, 1401, 602, 1404, 612, + 1428, 603, 1407, 606, 1413, 607, 1417, 609, 1422, + 610, 1424, 611, 1427, 611, 1426, 614, 1433, 615, + 1435, 615, 1434, 616, 1438, 618, 1442, 616, 1438, + 616, 1438, 617, 1440, 606, 1413}, + {578, 1349, 594, 1387, 596, 1391, 597, 1394, 597, + 1392, 598, 1395, 597, 1393, 598, 1395, 599, 1398, + 598, 1396, 601, 1402, 600, 1401, 602, 1404, 604, + 1409, 603, 1406, 605, 1413, 607, 1416, 609, 1421, + 610, 1423, 611, 1426, 611, 1425, 614, 1432, 615, + 1434, 614, 1434, 616, 1437, 625, 1459, 616, 1438, + 616, 1438, 617, 1440, 605, 1411}, + {582, 1357, 594, 1387, 596, 1391, 597, 1393, 596, + 1392, 598, 1395, 597, 1393, 598, 1394, 599, 1398, + 598, 1396, 601, 1401, 600, 1400, 601, 1403, 604, + 1409, 603, 1406, 606, 1413, 607, 1416, 609, 1421, + 610, 1423, 611, 1425, 611, 1425, 622, 1450, 615, + 1434, 615, 1434, 616, 1438, 618, 1443, 617, 1440, + 617, 1440, 619, 1445, 612, 1429}, + {587, 1370, 594, 1385, 595, 1388, 596, 1390, 595, + 1388, 596, 1391, 596, 1390, 596, 1392, 598, 1396, + 598, 1394, 600, 1400, 600, 1400, 602, 1405, 604, + 1410, 603, 1407, 606, 1415, 608, 1418, 610, 1424, + 611, 1427, 612, 1429, 613, 1429, 616, 1437, 625, + 1458, 617, 1441, 620, 1446, 622, 1452, 622, 1451, + 623, 1453, 626, 1461, 615, 1435}, + {738, 1721, 599, 1398, 595, 1388, 592, 1381, 597, + 1392, 588, 1373, 586, 1367, 585, 1365, 586, 1366, + 584, 1362, 585, 1365, 584, 1363, 585, 1364, 587, + 1369, 585, 1365, 587, 1370, 588, 1373, 590, 1376, + 591, 1378, 591, 1380, 591, 1378, 593, 1385, 594, + 1387, 598, 1396, 596, 1391, 598, 1396, 598, 1395, + 599, 1399, 613, 1431, 762, 1777}, + }, +/*disable cbc*/ + { + {821, 1915, 491, 1145, 491, 1146, 492, 1148, 492, + 1148, 492, 1149, 492, 1147, 492, 1149, 494, 1152, + 494, 1153, 496, 1158, 496, 1157, 498, 1161, 501, + 1168, 500, 1166, 503, 1174, 505, 1179, 508, 1185, + 509, 1188, 518, 1209, 511, 1192, 514, 1200, 516, + 1204, 516, 1204, 518, 1209, 521, 1216, 519, 1211, + 519, 1212, 523, 1220, 901, 2103}, + {598, 1395, 630, 1470, 631, 1473, 633, 1476, 632, + 1475, 633, 1477, 632, 1475, 633, 1476, 634, 1480, + 634, 1480, 637, 1486, 636, 1485, 638, 1488, 641, + 1496, 640, 1494, 644, 1502, 653, 1525, 648, 1512, + 650, 1516, 651, 1519, 651, 1520, 655, 1528, 657, + 1532, 657, 1533, 659, 1538, 662, 1544, 660, 1539, + 660, 1539, 662, 1546, 665, 1551}, + {598, 1394, 629, 1467, 631, 1472, 632, 1475, 632, + 1474, 632, 1476, 632, 1474, 632, 1475, 634, 1479, + 634, 1479, 636, 1485, 636, 1483, 637, 1487, 640, + 1494, 639, 1491, 650, 1518, 644, 1504, 647, 1510, + 648, 1513, 650, 1516, 650, 1517, 654, 1525, 655, + 1529, 655, 1529, 657, 1534, 660, 1539, 658, 1535, + 658, 1535, 660, 1540, 660, 1540}, + {598, 1395, 627, 1463, 629, 1469, 631, 1472, 631, + 1472, 631, 1473, 631, 1471, 631, 1473, 633, 1476, + 633, 1476, 635, 1482, 634, 1480, 636, 1484, 639, + 1491, 638, 1488, 641, 1496, 643, 1500, 646, 1506, + 647, 1510, 648, 1513, 649, 1513, 652, 1521, 654, + 1525, 661, 1543, 655, 1529, 658, 1534, 655, 1529, + 655, 1529, 658, 1535, 656, 1531}, + {598, 1395, 626, 1461, 629, 1467, 630, 1470, 630, + 1470, 631, 1471, 630, 1469, 630, 1471, 632, 1474, + 632, 1475, 634, 1480, 634, 1479, 635, 1482, 638, + 1489, 637, 1486, 640, 1494, 642, 1498, 645, 1504, + 654, 1526, 647, 1510, 648, 1511, 651, 1519, 652, + 1522, 652, 1522, 654, 1526, 656, 1531, 654, 1526, + 654, 1526, 656, 1531, 653, 1523}, + {599, 1398, 626, 1461, 628, 1466, 630, 1470, 630, + 1469, 630, 1471, 629, 1469, 630, 1470, 631, 1473, + 631, 1473, 634, 1479, 633, 1478, 635, 1481, 638, + 1488, 637, 1485, 640, 1493, 642, 1497, 644, 1503, + 645, 1506, 647, 1509, 647, 1509, 650, 1517, 652, + 1520, 652, 1520, 661, 1543, 655, 1529, 653, 1524, + 653, 1524, 655, 1529, 650, 1518}, + {601, 1401, 626, 1461, 629, 1467, 630, 1470, 630, + 1469, 630, 1470, 629, 1469, 630, 1470, 631, 1473, + 631, 1473, 634, 1479, 633, 1477, 635, 1481, 638, + 1488, 637, 1485, 640, 1493, 642, 1497, 644, 1503, + 645, 1505, 646, 1508, 654, 1526, 650, 1516, 651, + 1519, 651, 1519, 653, 1523, 655, 1528, 653, 1523, + 652, 1522, 654, 1527, 648, 1513}, + {603, 1406, 627, 1463, 629, 1468, 631, 1471, 630, + 1471, 631, 1472, 630, 1470, 630, 1471, 632, 1474, + 632, 1474, 634, 1479, 634, 1478, 635, 1482, 638, + 1489, 637, 1486, 640, 1494, 642, 1497, 652, 1522, + 645, 1506, 646, 1508, 647, 1509, 650, 1516, 651, + 1519, 651, 1519, 653, 1523, 655, 1528, 653, 1523, + 652, 1522, 654, 1526, 647, 1510}, + {605, 1411, 627, 1464, 630, 1469, 631, 1473, 631, + 1472, 631, 1473, 630, 1471, 631, 1473, 632, 1476, + 632, 1476, 635, 1481, 634, 1480, 636, 1483, 638, + 1490, 645, 1505, 640, 1494, 642, 1498, 644, 1504, + 646, 1506, 647, 1509, 647, 1509, 650, 1516, 651, + 1519, 651, 1518, 652, 1522, 654, 1527, 652, 1522, + 652, 1521, 654, 1526, 648, 1511}, + {607, 1417, 628, 1466, 630, 1471, 632, 1474, 632, + 1474, 632, 1475, 631, 1473, 632, 1475, 633, 1478, + 633, 1478, 636, 1483, 635, 1482, 636, 1485, 639, + 1492, 638, 1489, 641, 1495, 643, 1499, 645, 1505, + 646, 1507, 647, 1510, 647, 1510, 650, 1517, 651, + 1519, 651, 1519, 653, 1523, 654, 1527, 652, 1522, + 652, 1521, 662, 1544, 644, 1504}, + {612, 1427, 630, 1470, 632, 1475, 634, 1479, 633, + 1478, 634, 1479, 633, 1478, 634, 1479, 635, 1483, + 635, 1482, 637, 1487, 637, 1486, 638, 1489, 649, + 1514, 640, 1492, 642, 1499, 644, 1503, 646, 1508, + 647, 1510, 648, 1513, 648, 1513, 651, 1519, 652, + 1522, 652, 1521, 653, 1524, 655, 1528, 653, 1523, + 653, 1523, 655, 1528, 643, 1500}, + {614, 1432, 630, 1470, 632, 1475, 634, 1478, 633, + 1478, 634, 1479, 633, 1477, 634, 1479, 635, 1482, + 635, 1482, 638, 1488, 637, 1486, 638, 1489, 641, + 1495, 639, 1492, 642, 1499, 644, 1502, 646, 1508, + 647, 1510, 648, 1512, 648, 1512, 651, 1519, 652, + 1521, 652, 1521, 653, 1524, 663, 1547, 653, 1523, + 653, 1523, 655, 1528, 642, 1499}, + {617, 1440, 630, 1470, 632, 1474, 633, 1477, 633, + 1477, 634, 1478, 633, 1477, 634, 1478, 635, 1482, + 635, 1482, 637, 1487, 637, 1486, 638, 1489, 641, + 1495, 640, 1492, 643, 1499, 644, 1503, 646, 1508, + 647, 1510, 648, 1512, 648, 1512, 659, 1537, 652, + 1522, 652, 1521, 653, 1525, 656, 1530, 654, 1525, + 654, 1525, 657, 1533, 650, 1517}, + {622, 1452, 629, 1469, 630, 1471, 632, 1474, 631, + 1473, 632, 1475, 631, 1473, 632, 1475, 634, 1479, + 634, 1480, 637, 1486, 636, 1485, 639, 1491, 641, + 1496, 640, 1493, 643, 1501, 645, 1505, 647, 1510, + 648, 1513, 650, 1516, 650, 1516, 653, 1523, 662, + 1546, 655, 1528, 657, 1533, 660, 1539, 658, 1536, + 659, 1538, 664, 1549, 653, 1523}, + {892, 2082, 636, 1484, 632, 1474, 629, 1468, 634, + 1480, 625, 1459, 623, 1453, 622, 1452, 623, 1453, + 622, 1451, 623, 1453, 622, 1451, 622, 1452, 624, + 1457, 623, 1453, 625, 1459, 626, 1461, 628, 1465, + 629, 1467, 629, 1468, 629, 1467, 631, 1473, 633, + 1476, 636, 1485, 634, 1479, 637, 1485, 635, 1483, + 637, 1486, 652, 1520, 918, 2143}, + } +}; + +const int16_t baseline_cap_data_old[2][TX_NUMBER][RX_NUMBER*2] = { + /*enable cbc*/ + { + {557, 1035, 604, 1122, 605, 1123, 604, 1122, 608, 1130, 601, + 1117, 608, 1130, 615, 1141, 613, 1139, 614, 1140, 610, 1132, + 609, 1131, 620, 1151, 625, 1161, 638, 1184, 641, 1191, 648, + 1203, 669, 1243, 661, 1227, 671, 1245, 676, 1256, 685, 1273, + 690, 1282, 694, 1288, 689, 1279, 696, 1292, 696, 1292, 724, + 1344, 682, 1266, 724, 1344}, + {574, 1066, 622, 1154, 622, 1156, 620, 1151, 624, 1158, 618, + 1148, 625, 1161, 631, 1173, 630, 1170, 630, 1170, 627, 1164, + 624, 1160, 636, 1180, 641, 1191, 653, 1213, 656, 1218, 662, + 1230, 676, 1255, 676, 1255, 687, 1275, 694, 1288, 703, 1305, + 707, 1313, 721, 1339, 706, 1310, 711, 1321, 711, 1321, 734, + 1364, 671, 1245, 694, 1288}, + {573, 1065, 619, 1149, 618, 1148, 617, 1147, 620, 1152, 614, + 1140, 622, 1154, 628, 1166, 629, 1167, 628, 1166, 623, 1157, + 622, 1154, 633, 1175, 638, 1186, 661, 1227, 653, 1213, 660, + 1226, 673, 1249, 673, 1249, 684, 1270, 690, 1282, 701, 1301, + 704, 1307, 707, 1313, 701, 1303, 707, 1313, 707, 1313, 729, + 1355, 666, 1238, 687, 1277}, + {573, 1063, 616, 1144, 616, 1144, 614, 1140, 618, 1148, 611, + 1135, 619, 1149, 624, 1160, 625, 1161, 625, 1161, 622, 1154, + 620, 1152, 630, 1170, 637, 1183, 650, 1206, 652, 1210, 659, + 1223, 670, 1244, 671, 1247, 683, 1268, 688, 1278, 706, 1312, + 701, 1301, 704, 1308, 699, 1297, 704, 1308, 703, 1305, 724, + 1344, 659, 1223, 673, 1249}, + {573, 1063, 615, 1141, 615, 1141, 613, 1138, 616, 1144, 610, + 1132, 618, 1148, 624, 1158, 624, 1158, 624, 1160, 622, 1154, + 620, 1151, 630, 1170, 636, 1182, 648, 1204, 651, 1209, 659, + 1223, 670, 1244, 671, 1245, 680, 1264, 696, 1292, 694, 1290, + 699, 1297, 701, 1303, 696, 1292, 701, 1301, 699, 1297, 718, + 1333, 649, 1205, 663, 1231}, + {574, 1066, 615, 1143, 615, 1143, 613, 1139, 616, 1144, 610, + 1134, 617, 1147, 624, 1158, 625, 1161, 625, 1161, 622, 1156, + 622, 1154, 631, 1173, 646, 1200, 650, 1206, 651, 1209, 659, + 1223, 671, 1247, 671, 1245, 681, 1265, 687, 1275, 694, 1288, + 697, 1295, 699, 1297, 694, 1288, 699, 1297, 697, 1295, 713, + 1325, 645, 1199, 658, 1222}, + {578, 1073, 617, 1147, 616, 1144, 615, 1141, 618, 1148, 611, + 1135, 619, 1149, 625, 1161, 636, 1180, 628, 1166, 624, 1158, + 624, 1158, 634, 1177, 638, 1186, 651, 1209, 652, 1212, 660, + 1226, 673, 1249, 673, 1249, 681, 1265, 687, 1275, 694, 1288, + 697, 1295, 699, 1299, 694, 1290, 699, 1297, 697, 1294, 713, + 1323, 643, 1193, 654, 1214}, + {582, 1080, 619, 1149, 619, 1149, 616, 1144, 619, 1149, 613, + 1139, 620, 1152, 627, 1165, 628, 1166, 629, 1167, 625, 1161, + 624, 1160, 636, 1180, 641, 1191, 652, 1212, 655, 1216, 661, + 1227, 673, 1251, 673, 1251, 692, 1284, 687, 1275, 693, 1287, + 697, 1294, 699, 1299, 694, 1288, 699, 1297, 698, 1296, 713, + 1325, 645, 1197, 655, 1216}, + {583, 1083, 619, 1149, 618, 1148, 615, 1143, 620, 1151, 613, + 1138, 620, 1152, 627, 1164, 627, 1165, 638, 1184, 624, 1160, + 624, 1158, 634, 1178, 638, 1186, 652, 1210, 653, 1213, 660, + 1226, 671, 1245, 671, 1245, 679, 1261, 683, 1269, 690, 1282, + 694, 1288, 696, 1292, 690, 1281, 694, 1290, 693, 1287, 707, + 1313, 649, 1205, 655, 1217}, + {589, 1095, 623, 1157, 622, 1156, 619, 1149, 623, 1157, 616, + 1144, 624, 1158, 630, 1170, 631, 1173, 631, 1173, 629, 1167, + 627, 1165, 638, 1186, 643, 1193, 655, 1216, 666, 1236, 662, + 1230, 673, 1251, 673, 1249, 680, 1264, 684, 1270, 692, 1284, + 697, 1294, 697, 1295, 692, 1286, 697, 1295, 696, 1292, 707, + 1313, 650, 1206, 655, 1216}, + {594, 1104, 628, 1166, 624, 1160, 622, 1156, 627, 1164, 618, + 1148, 627, 1164, 632, 1174, 634, 1177, 634, 1178, 630, 1170, + 629, 1169, 641, 1190, 644, 1196, 656, 1218, 659, 1223, 664, + 1234, 675, 1253, 673, 1251, 681, 1265, 685, 1271, 693, 1287, + 694, 1290, 697, 1295, 693, 1287, 699, 1299, 708, 1316, 710, + 1318, 649, 1205, 652, 1210}, + {597, 1109, 629, 1169, 628, 1166, 624, 1160, 628, 1166, 620, + 1152, 627, 1165, 633, 1175, 634, 1178, 634, 1178, 641, 1191, + 631, 1173, 641, 1191, 646, 1200, 659, 1223, 661, 1227, 666, + 1236, 676, 1255, 676, 1256, 681, 1265, 685, 1273, 692, 1286, + 713, 1325, 699, 1297, 694, 1288, 701, 1301, 701, 1301, 708, + 1316, 646, 1200, 649, 1205}, + {601, 1115, 630, 1170, 629, 1167, 625, 1161, 628, 1166, 620, + 1151, 628, 1166, 633, 1175, 634, 1178, 636, 1180, 631, 1173, + 630, 1170, 641, 1191, 646, 1200, 659, 1225, 660, 1226, 674, + 1252, 676, 1255, 673, 1251, 680, 1264, 685, 1271, 692, 1286, + 694, 1290, 698, 1296, 693, 1287, 699, 1299, 702, 1304, 713, + 1323, 645, 1199, 647, 1201}, + {606, 1125, 632, 1174, 629, 1169, 627, 1164, 629, 1169, 622, + 1154, 629, 1167, 634, 1178, 634, 1178, 637, 1183, 633, 1175, + 630, 1170, 642, 1192, 647, 1201, 659, 1223, 660, 1226, 666, + 1236, 676, 1255, 673, 1251, 681, 1265, 685, 1271, 693, 1287, + 697, 1294, 701, 1301, 704, 1308, 702, 1304, 706, 1310, 718, + 1334, 638, 1184, 641, 1190}, + {613, 1138, 634, 1177, 631, 1173, 630, 1170, 632, 1174, 624, + 1158, 633, 1175, 637, 1183, 638, 1186, 641, 1190, 636, 1180, + 637, 1183, 645, 1199, 651, 1209, 662, 1229, 664, 1232, 670, + 1244, 680, 1262, 680, 1262, 688, 1278, 692, 1284, 699, 1297, + 704, 1307, 707, 1313, 704, 1307, 712, 1322, 715, 1329, 743, + 1379, 643, 1193, 644, 1196}, + {629, 1169, 639, 1187, 634, 1178, 629, 1169, 629, 1169, 620, + 1152, 627, 1165, 641, 1191, 630, 1170, 633, 1175, 627, 1165, + 624, 1160, 634, 1178, 639, 1187, 656, 1218, 652, 1210, 657, + 1221, 667, 1239, 678, 1258, 673, 1251, 678, 1258, 685, 1271, + 706, 1310, 692, 1284, 689, 1279, 697, 1294, 701, 1301, 727, + 1349, 643, 1193, 644, 1196}, + {757, 1407, 748, 1388, 705, 1309, 698, 1296, 686, 1274, 678, + 1260, 685, 1271, 683, 1269, 661, 1227, 671, 1245, 655, 1217, + 669, 1242, 659, 1223, 665, 1235, 665, 1235, 661, 1227, 649, + 1205, 646, 1200, 641, 1191, 649, 1205, 664, 1232, 629, 1167, + 645, 1199, 629, 1167, 645, 1199, 622, 1154, 629, 1167, 629, + 1167, 500, 3000, 500, 3000}, + }, + /*disable cbc*/ + { + {557, 1035, 604, 1122, 605, 1123, 604, 1122, 608, 1130, 601, + 1117, 608, 1130, 615, 1141, 613, 1139, 614, 1140, 610, 1132, + 609, 1131, 620, 1151, 625, 1161, 638, 1184, 641, 1191, 648, + 1203, 669, 1243, 661, 1227, 671, 1245, 676, 1256, 685, 1273, + 690, 1282, 694, 1288, 689, 1279, 696, 1292, 696, 1292, 724, + 1344, 682, 1266, 724, 1344}, + {574, 1066, 622, 1154, 622, 1156, 620, 1151, 624, 1158, 618, + 1148, 625, 1161, 631, 1173, 630, 1170, 630, 1170, 627, 1164, + 624, 1160, 636, 1180, 641, 1191, 653, 1213, 656, 1218, 662, + 1230, 676, 1255, 676, 1255, 687, 1275, 694, 1288, 703, 1305, + 707, 1313, 721, 1339, 706, 1310, 711, 1321, 711, 1321, 734, + 1364, 671, 1245, 694, 1288}, + {573, 1065, 619, 1149, 618, 1148, 617, 1147, 620, 1152, 614, + 1140, 622, 1154, 628, 1166, 629, 1167, 628, 1166, 623, 1157, + 622, 1154, 633, 1175, 638, 1186, 661, 1227, 653, 1213, 660, + 1226, 673, 1249, 673, 1249, 684, 1270, 690, 1282, 701, 1301, + 704, 1307, 707, 1313, 701, 1303, 707, 1313, 707, 1313, 729, + 1355, 666, 1238, 687, 1277}, + {573, 1063, 616, 1144, 616, 1144, 614, 1140, 618, 1148, 611, + 1135, 619, 1149, 624, 1160, 625, 1161, 625, 1161, 622, 1154, + 620, 1152, 630, 1170, 637, 1183, 650, 1206, 652, 1210, 659, + 1223, 670, 1244, 671, 1247, 683, 1268, 688, 1278, 706, 1312, + 701, 1301, 704, 1308, 699, 1297, 704, 1308, 703, 1305, 724, + 1344, 659, 1223, 673, 1249}, + {573, 1063, 615, 1141, 615, 1141, 613, 1138, 616, 1144, 610, + 1132, 618, 1148, 624, 1158, 624, 1158, 624, 1160, 622, 1154, + 620, 1151, 630, 1170, 636, 1182, 648, 1204, 651, 1209, 659, + 1223, 670, 1244, 671, 1245, 680, 1264, 696, 1292, 694, 1290, + 699, 1297, 701, 1303, 696, 1292, 701, 1301, 699, 1297, 718, + 1333, 649, 1205, 663, 1231}, + {574, 1066, 615, 1143, 615, 1143, 613, 1139, 616, 1144, 610, + 1134, 617, 1147, 624, 1158, 625, 1161, 625, 1161, 622, 1156, + 622, 1154, 631, 1173, 646, 1200, 650, 1206, 651, 1209, 659, + 1223, 671, 1247, 671, 1245, 681, 1265, 687, 1275, 694, 1288, + 697, 1295, 699, 1297, 694, 1288, 699, 1297, 697, 1295, 713, + 1325, 645, 1199, 658, 1222}, + {578, 1073, 617, 1147, 616, 1144, 615, 1141, 618, 1148, 611, + 1135, 619, 1149, 625, 1161, 636, 1180, 628, 1166, 624, 1158, + 624, 1158, 634, 1177, 638, 1186, 651, 1209, 652, 1212, 660, + 1226, 673, 1249, 673, 1249, 681, 1265, 687, 1275, 694, 1288, + 697, 1295, 699, 1299, 694, 1290, 699, 1297, 697, 1294, 713, + 1323, 643, 1193, 654, 1214}, + {582, 1080, 619, 1149, 619, 1149, 616, 1144, 619, 1149, 613, + 1139, 620, 1152, 627, 1165, 628, 1166, 629, 1167, 625, 1161, + 624, 1160, 636, 1180, 641, 1191, 652, 1212, 655, 1216, 661, + 1227, 673, 1251, 673, 1251, 692, 1284, 687, 1275, 693, 1287, + 697, 1294, 699, 1299, 694, 1288, 699, 1297, 698, 1296, 713, + 1325, 645, 1197, 655, 1216}, + {583, 1083, 619, 1149, 618, 1148, 615, 1143, 620, 1151, 613, + 1138, 620, 1152, 627, 1164, 627, 1165, 638, 1184, 624, 1160, + 624, 1158, 634, 1178, 638, 1186, 652, 1210, 653, 1213, 660, + 1226, 671, 1245, 671, 1245, 679, 1261, 683, 1269, 690, 1282, + 694, 1288, 696, 1292, 690, 1281, 694, 1290, 693, 1287, 707, + 1313, 649, 1205, 655, 1217}, + {589, 1095, 623, 1157, 622, 1156, 619, 1149, 623, 1157, 616, + 1144, 624, 1158, 630, 1170, 631, 1173, 631, 1173, 629, 1167, + 627, 1165, 638, 1186, 643, 1193, 655, 1216, 666, 1236, 662, + 1230, 673, 1251, 673, 1249, 680, 1264, 684, 1270, 692, 1284, + 697, 1294, 697, 1295, 692, 1286, 697, 1295, 696, 1292, 707, + 1313, 650, 1206, 655, 1216}, + {594, 1104, 628, 1166, 624, 1160, 622, 1156, 627, 1164, 618, + 1148, 627, 1164, 632, 1174, 634, 1177, 634, 1178, 630, 1170, + 629, 1169, 641, 1190, 644, 1196, 656, 1218, 659, 1223, 664, + 1234, 675, 1253, 673, 1251, 681, 1265, 685, 1271, 693, 1287, + 694, 1290, 697, 1295, 693, 1287, 699, 1299, 708, 1316, 710, + 1318, 649, 1205, 652, 1210}, + {597, 1109, 629, 1169, 628, 1166, 624, 1160, 628, 1166, 620, + 1152, 627, 1165, 633, 1175, 634, 1178, 634, 1178, 641, 1191, + 631, 1173, 641, 1191, 646, 1200, 659, 1223, 661, 1227, 666, + 1236, 676, 1255, 676, 1256, 681, 1265, 685, 1273, 692, 1286, + 713, 1325, 699, 1297, 694, 1288, 701, 1301, 701, 1301, 708, + 1316, 646, 1200, 649, 1205}, + {601, 1115, 630, 1170, 629, 1167, 625, 1161, 628, 1166, 620, + 1151, 628, 1166, 633, 1175, 634, 1178, 636, 1180, 631, 1173, + 630, 1170, 641, 1191, 646, 1200, 659, 1225, 660, 1226, 674, + 1252, 676, 1255, 673, 1251, 680, 1264, 685, 1271, 692, 1286, + 694, 1290, 698, 1296, 693, 1287, 699, 1299, 702, 1304, 713, + 1323, 645, 1199, 647, 1201}, + {606, 1125, 632, 1174, 629, 1169, 627, 1164, 629, 1169, 622, + 1154, 629, 1167, 634, 1178, 634, 1178, 637, 1183, 633, 1175, + 630, 1170, 642, 1192, 647, 1201, 659, 1223, 660, 1226, 666, + 1236, 676, 1255, 673, 1251, 681, 1265, 685, 1271, 693, 1287, + 697, 1294, 701, 1301, 704, 1308, 702, 1304, 706, 1310, 718, + 1334, 638, 1184, 641, 1190}, + {613, 1138, 634, 1177, 631, 1173, 630, 1170, 632, 1174, 624, + 1158, 633, 1175, 637, 1183, 638, 1186, 641, 1190, 636, 1180, + 637, 1183, 645, 1199, 651, 1209, 662, 1229, 664, 1232, 670, + 1244, 680, 1262, 680, 1262, 688, 1278, 692, 1284, 699, 1297, + 704, 1307, 707, 1313, 704, 1307, 712, 1322, 715, 1329, 743, + 1379, 643, 1193, 644, 1196}, + {629, 1169, 639, 1187, 634, 1178, 629, 1169, 629, 1169, 620, + 1152, 627, 1165, 641, 1191, 630, 1170, 633, 1175, 627, 1165, + 624, 1160, 634, 1178, 639, 1187, 656, 1218, 652, 1210, 657, + 1221, 667, 1239, 678, 1258, 673, 1251, 678, 1258, 685, 1271, + 706, 1310, 692, 1284, 689, 1279, 697, 1294, 701, 1301, 727, + 1349, 643, 1193, 644, 1196}, + {757, 1407, 748, 1388, 705, 1309, 698, 1296, 686, 1274, 678, + 1260, 685, 1271, 683, 1269, 661, 1227, 671, 1245, 655, 1217, + 669, 1242, 659, 1223, 665, 1235, 665, 1235, 661, 1227, 649, + 1205, 646, 1200, 641, 1191, 649, 1205, 664, 1232, 629, 1167, + 645, 1199, 629, 1167, 645, 1199, 622, 1154, 629, 1167, 629, + 1167, 500, 3000, 500, 3000}, + } + +}; + +#endif diff --git a/drivers/input/touchscreen/synaptics_driver_s3320.c b/drivers/input/touchscreen/synaptics_driver_s3320.c new file mode 100755 index 000000000000..a9e08040cfc5 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_driver_s3320.c @@ -0,0 +1,5287 @@ +/****************************************************************************** + ** File: - /android/kernel/drivers/input/touchscreen/synaptic_s3320.c + ** Copyright (C), 2008-2012, OEM Mobile Comm Corp., Ltd + ** + ** Description: + ** touch panel driver for synaptics + ** can change MAX_POINT_NUM value to support multipoint + ** Version: 1.0 + ** Date created: 10:49:46,18/01/2012 + ** Author: Yixue.Ge@BasicDrv.TP + ** + ** ------------------------ Revision History: ----------------------------- + ** + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_FB +#include +#include +#endif + +#include + +#include "synaptics_redremote.h" +#include +#include "synaptics_baseline.h" + +/*----------------------Global Define--------------------------------*/ + + +#define TP_UNKNOWN 0 +#define TP_G2Y 1 +#define TP_TPK 2 +#define TP_TRULY 3 +#define TP_OFILM 4 +#define TP_JDI_TPK 6 +#define TP_TEST_ENABLE 1 + +#define DiagonalUpperLimit 1100 +#define DiagonalLowerLimit 900 + +#define PAGESIZE 512 +#define TPD_USE_EINT + +#define TPD_DEVICE "synaptics,s3320" + +/*#define SUPPORT_SLEEP_POWEROFF*/ +#define SUPPORT_GESTURE +#define RESET_ONESECOND +/*#define SUPPORT_GLOVES_MODE*/ +/*#define REPORT_2D_PRESSURE*/ +/*#define SUPPORT_VIRTUAL_KEY*/ + + +#define SUPPORT_TP_SLEEP_MODE +#define TYPE_B_PROTOCOL /*Multi-finger operation*/ +#define TP_FW_NAME_MAX_LEN 128 +#define SUPPORT_TP_TOUCHKEY + +#define TEST_MAGIC1 0x494D494C +#define TEST_MAGIC2 0x474D4954 + +struct test_header { + unsigned int magic1; + unsigned int magic2; + unsigned int withCBC; + unsigned int array_limit_offset; + unsigned int array_limit_size; + unsigned int array_limitcbc_offset; + unsigned int array_limitcbc_size; +}; + +/******************for Red function*****************/ +#define CONFIG_SYNAPTIC_RED + +/*********************for gesture*******************/ +#ifdef SUPPORT_GESTURE +#define ENABLE_UNICODE 0x40 +#define ENABLE_VEE 0x20 +#define ENABLE_CIRCLE 0x08 +#define ENABLE_SWIPE 0x02 +#define ENABLE_DTAP 0x01 + +#define UNICODE_DETECT 0x0b +#define VEE_DETECT 0x0a +#define CIRCLE_DETECT 0x08 +#define SWIPE_DETECT 0x07 +#define DTAP_DETECT 0x03 + + +#define UnkownGestrue 0 +#define DouTap 1 /* double tap */ +#define UpVee 2 /* V */ +#define DownVee 3 /* ^ */ +#define LeftVee 4 /* > */ +#define RightVee 5 /* < */ +#define Circle 6 /* O */ +#define DouSwip 7 /* || */ +#define Left2RightSwip 8 /* --> */ +#define Right2LeftSwip 9 /* <-- */ +#define Up2DownSwip 10 /* |v */ +#define Down2UpSwip 11 /* |^ */ +#define Mgestrue 12 /* M */ +#define Wgestrue 13 /* W */ +#define Sgestrue 14 /* S */ + +#define KEY_GESTURE_W 246 /* w */ +#define KEY_GESTURE_M 247 /* m */ +#define KEY_GESTURE_S 248 /* s */ +#define KEY_DOUBLE_TAP 249 /* double tap to wake */ +#define KEY_GESTURE_CIRCLE 250 /* draw circle to lunch camera */ +#define KEY_GESTURE_TWO_SWIPE 251 /*swipe two finger vertically to play/pause*/ +#define KEY_GESTURE_V 252 /* draw v to toggle flashlight */ +#define KEY_GESTURE_LEFT_V 253 /* draw left arrow for previous track */ +#define KEY_GESTURE_RIGHT_V 254 /* draw right arrow for next track */ + +#define BIT0 (0x1 << 0) +#define BIT1 (0x1 << 1) +#define BIT2 (0x1 << 2) +#define BIT3 (0x1 << 3) +#define BIT4 (0x1 << 4) +#define BIT5 (0x1 << 5) +#define BIT6 (0x1 << 6) +#define BIT7 (0x1 << 7) + +int LeftVee_gesture; /* > */ +int RightVee_gesture; /* < */ +int DouSwip_gesture; /* || */ +int Circle_gesture; /* O */ +int UpVee_gesture; /* V */ +int DownVee_gesture; /* ^ */ +int DouTap_gesture; /* double tap */ + +int Left2RightSwip_gesture;/* --> */ +int Right2LeftSwip_gesture;/* <-- */ +int Up2DownSwip_gesture;/* |v */ +int Down2UpSwip_gesture;/* |^ */ + +int Wgestrue_gesture;/* W */ +int Mgestrue_gesture;/* M */ +int Sgestrue_gesture;/* S */ +static int gesture_switch; +#endif + +/*********************for Debug LOG switch*******************/ +#define TPD_ERR(a, arg...) pr_err(TPD_DEVICE ": " a, ##arg) +#define TPDTM_DMESG(a, arg...) printk(TPD_DEVICE ": " a, ##arg) + +#define TPD_DEBUG(a, arg...)\ + do {\ + if (tp_debug)\ + pr_err(TPD_DEVICE ": " a, ##arg);\ + } while (0) + +/*-------------------------------Global Variable-----------------------------*/ +static int baseline_ret; +static int TP_FW; +static int tp_dev; +static unsigned int tp_debug; +static int button_map[3]; +static int tx_rx_num[2]; +static int16_t Rxdata[30][30]; +static int16_t delta_baseline[30][30]; +static int16_t baseline[30][30]; +static int16_t delta[30][30]; +static int TX_NUM; +static int RX_NUM; +static int report_key_point_y; +static int force_update; +static int LCD_WIDTH; +static int LCD_HEIGHT; +static int get_tp_base; +#define ENABLE_TPEDGE_LIMIT +#ifdef ENABLE_TPEDGE_LIMIT +static int limit_enable = 1; +static void synaptics_tpedge_limitfunc(void); +#endif +/*static int ch_getbase_status = 0;*/ +/*struct timeval start_time,end_time;*/ + +#ifdef SUPPORT_TP_SLEEP_MODE +static int sleep_enable; +#endif +#ifdef SUPPORT_TP_TOUCHKEY +static int key_switch; +static bool key_back_disable, key_appselect_disable; +#endif +static struct synaptics_ts_data *ts_g; +static struct workqueue_struct *synaptics_wq; +static struct workqueue_struct *synaptics_report; +static struct workqueue_struct *get_base_report; +static struct proc_dir_entry *prEntry_tp; + + +#ifdef SUPPORT_GESTURE +static uint32_t clockwise; +static uint32_t gesture; + +static uint32_t gesture_upload; + +/****point position*****/ +struct Coordinate { + uint32_t x; + uint32_t y; +}; +static struct Coordinate Point_start; +static struct Coordinate Point_end; +static struct Coordinate Point_1st; +static struct Coordinate Point_2nd; +static struct Coordinate Point_3rd; +static struct Coordinate Point_4th; +#endif + +/*-------------------------Global Registers------------------------------*/ +static unsigned short SynaF34DataBase; +static unsigned short SynaF34QueryBase; +static unsigned short SynaF01DataBase; +static unsigned short SynaF01CommandBase; + +static unsigned short SynaF34Reflash_BlockNum; +static unsigned short SynaF34Reflash_BlockData; +static unsigned short SynaF34ReflashQuery_BootID; +static unsigned short SynaF34ReflashQuery_FlashPropertyQuery; +static unsigned short SynaF34ReflashQuery_FirmwareBlockSize; +static unsigned short SynaF34ReflashQuery_FirmwareBlockCount; +static unsigned short SynaF34ReflashQuery_ConfigBlockSize; +static unsigned short SynaF34ReflashQuery_ConfigBlockCount; + +static unsigned short SynaFirmwareBlockSize; +static unsigned short SynaF34_FlashControl; + +static int F01_RMI_QUERY_BASE; +static int F01_RMI_CMD_BASE; +static int F01_RMI_CTRL_BASE; +static int F01_RMI_DATA_BASE; + +static int F12_2D_QUERY_BASE; +static int F12_2D_CMD_BASE; +static int F12_2D_CTRL_BASE; +static int F12_2D_DATA_BASE; +static int F12_2D_DATA15; + +static int F34_FLASH_QUERY_BASE; +static int F34_FLASH_CMD_BASE; +static int F34_FLASH_CTRL_BASE; +static int F34_FLASH_DATA_BASE; + +static int F51_CUSTOM_QUERY_BASE; +static int F51_CUSTOM_CMD_BASE; +static int F51_CUSTOM_CTRL_BASE; +static int F51_CUSTOM_DATA_BASE; + +static int F01_RMI_QUERY11; +static int F01_RMI_DATA01; +static int F01_RMI_CMD00; +static int F01_RMI_CTRL00; +static int F01_RMI_CTRL01; +static int F01_RMI_CTRL02; + +static int F12_2D_CTRL08; +static int F12_2D_CTRL32; +static int F12_2D_DATA04; +static int F12_2D_DATA38; +static int F12_2D_DATA39; +static int F12_2D_CMD00; +static int F12_2D_CTRL20; +static int F12_2D_CTRL27; + +static int F34_FLASH_CTRL00; + +static int F51_CUSTOM_CTRL00; +static int F51_CUSTOM_DATA04; +static int F51_CUSTOM_DATA11; +static int version_is_s3508; +#if TP_TEST_ENABLE +static int F54_ANALOG_QUERY_BASE;/*0x73*/ +static int F54_ANALOG_COMMAND_BASE;/*0x72*/ +static int F54_ANALOG_CONTROL_BASE;/*0x0d*/ +static int F54_ANALOG_DATA_BASE;/*0x00*/ +#endif + +/*-------------------------Function Declare----------------------------*/ +static int synaptics_i2c_suspend(struct device *dev); +static int synaptics_i2c_resume(struct device *dev); +/**************I2C resume && suspend end*********/ +static void speedup_synaptics_resume(struct work_struct *work); +static int synaptics_ts_resume(struct device *dev); +static int synaptics_ts_suspend(struct device *dev); +static int synaptics_ts_remove(struct i2c_client *client); +static int synaptics_ts_probe(struct i2c_client *client, +const struct i2c_device_id *id); +static ssize_t synaptics_rmi4_baseline_show(struct device *dev, +char *buf, bool savefile); +static ssize_t synaptics_rmi4_vendor_id_show(struct device *dev, +struct device_attribute *attr, char *buf); +static int synapitcs_ts_update(struct i2c_client *client, +const uint8_t *data, uint32_t data_len, bool force); + +static int synaptics_rmi4_i2c_read_block(struct i2c_client *client, +unsigned char addr, unsigned short length, unsigned char *data); + +static int synaptics_rmi4_i2c_write_block(struct i2c_client *client, +unsigned char addr, unsigned short length, unsigned char const *data); + +static int synaptics_rmi4_i2c_read_byte(struct i2c_client *client, + unsigned char addr); + +static int synaptics_rmi4_i2c_write_byte(struct i2c_client *client, + unsigned char addr, unsigned char data); + +static int synaptics_rmi4_i2c_read_word(struct i2c_client *client, + unsigned char addr); + +static int synaptics_rmi4_i2c_write_word(struct i2c_client *client, + unsigned char addr, unsigned short data); +static int synaptics_mode_change(int mode); + +#ifdef TPD_USE_EINT +static irqreturn_t synaptics_irq_thread_fn(int irq, void *dev_id); +#endif + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, +unsigned long event, void *data); +#endif +static int synaptics_soft_reset(struct synaptics_ts_data *ts); +static void synaptics_hard_reset(struct synaptics_ts_data *ts); +static int set_changer_bit(struct synaptics_ts_data *ts); +static int tp_baseline_get(struct synaptics_ts_data *ts, bool flag); + +/*----------------------------Using Struct------------------------------*/ +struct point_info { + unsigned char status; + int x; + int raw_x; + int y; + int raw_y; + int z; +#ifdef REPORT_2D_PRESSURE + unsigned char pressure; +#endif +}; + +static const struct i2c_device_id synaptics_ts_id[] = { + { TPD_DEVICE, 0 }, + { } +}; + +static const struct of_device_id synaptics_match_table[] = { + { .compatible = TPD_DEVICE,}, + { }, +}; + +static const struct dev_pm_ops synaptic_pm_ops = { +#ifdef CONFIG_PM + .suspend = synaptics_i2c_suspend, + .resume = synaptics_i2c_resume, +#else + .suspend = NULL, + .resume = NULL, +#endif +}; + +static int probe_ret; +struct synaptics_optimize_data { + struct delayed_work work; + struct workqueue_struct *workqueue; + struct i2c_client *client; + const struct i2c_device_id *dev_id; +}; +static struct synaptics_optimize_data optimize_data; +static void synaptics_ts_probe_func(struct work_struct *w) +{ + struct i2c_client *client_optimize = optimize_data.client; + const struct i2c_device_id *dev_id = optimize_data.dev_id; + + TPD_ERR("after on cpu [%d]\n", smp_processor_id()); + probe_ret = synaptics_ts_probe(client_optimize, dev_id); +} + +static int oem_synaptics_ts_probe(struct i2c_client *client, +const struct i2c_device_id *id) +{ + int i; + + optimize_data.client = client; + optimize_data.dev_id = id; + optimize_data.workqueue = create_workqueue("tpd_probe_optimize"); + + INIT_DELAYED_WORK(&(optimize_data.work), synaptics_ts_probe_func); + TPD_ERR("before on cpu [%d]\n", smp_processor_id()); + + for_each_possible_cpu(i) { + TPD_ERR("check CPU[%d] is [%s]\n", + i, cpu_is_offline(i)?"offline":"online"); + + if (cpu_online(i) && (i != smp_processor_id())) + break; + } + queue_delayed_work_on(i, optimize_data.workqueue, + &(optimize_data.work), msecs_to_jiffies(300)); + + return probe_ret; +} + +static struct i2c_driver tpd_i2c_driver = { + .probe = oem_synaptics_ts_probe, + .remove = synaptics_ts_remove, + .id_table = synaptics_ts_id, + .driver = { + .name = TPD_DEVICE, + .of_match_table = synaptics_match_table, + .pm = &synaptic_pm_ops, + }, +}; + +struct synaptics_ts_data { + struct i2c_client *client; + struct mutex mutex; + struct mutex mutexreport; + int irq; + int irq_gpio; + atomic_t irq_enable; + int id1_gpio; + int id2_gpio; + int id3_gpio; + int reset_gpio; + int v1p8_gpio; + int support_hw_poweroff; + int support_1080x2160_tp; + int enable2v8_gpio; + int max_num; + int enable_remote; + int regulator_vdd_vmin; + int regulator_vdd_vmax; + int regulator_vdd_current; + int regulator_avdd_vmin; + int regulator_avdd_vmax; + int regulator_avdd_current; + + uint32_t irq_flags; + uint32_t max_x; + uint32_t max_y; + uint32_t max_y_real; + uint32_t btn_state; + uint32_t pre_finger_state; + uint32_t pre_btn_state; + struct delayed_work base_work; + struct work_struct report_work; + struct delayed_work speed_up_work; + struct input_dev *input_dev; + struct hrtimer timer; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#endif + /******gesture*******/ + int gesture_enable; + int in_gesture_mode; + int glove_enable; + int changer_connet; + int is_suspended; + atomic_t is_stop; + spinlock_t lock; + + /********test*******/ + int i2c_device_test; + + /******power*******/ + struct regulator *vdd_2v8; + struct regulator *vcc_i2c_1v8; + + /*pinctrl******/ + struct device *dev; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + + /*******for FW update*******/ + bool loading_fw; + bool support_ft;/*support force touch*/ + char fw_name[TP_FW_NAME_MAX_LEN]; + char test_limit_name[TP_FW_NAME_MAX_LEN]; + char fw_id[12]; + char manu_name[30]; +#ifdef SUPPORT_VIRTUAL_KEY + struct kobject *properties_kobj; +#endif +}; + +static struct device_attribute attrs_oem[] = { + __ATTR(vendor_id, 0664, synaptics_rmi4_vendor_id_show, NULL), +}; + +static void touch_enable(struct synaptics_ts_data *ts) +{ + spin_lock(&ts->lock); + if (atomic_read(&ts->irq_enable) == 0) { + if (ts->irq) + enable_irq(ts->irq); + atomic_set(&ts->irq_enable, 1); + } + spin_unlock(&ts->lock); +} + +static void touch_disable(struct synaptics_ts_data *ts) +{ + spin_lock(&ts->lock); + if (atomic_read(&ts->irq_enable) == 1) { + if (ts->irq) + disable_irq_nosync(ts->irq); + atomic_set(&ts->irq_enable, 0); + } + spin_unlock(&ts->lock); +} + +static int tpd_hw_pwron(struct synaptics_ts_data *ts) +{ + int rc; + + /***enable the 2v8 power*****/ + if (!IS_ERR(ts->vdd_2v8)) { + rc = regulator_enable(ts->vdd_2v8); + if (rc) + TPD_ERR("Regulator vdd enable failed rc=%d\n", rc); + } + if (ts->v1p8_gpio > 0) { + TPD_DEBUG("synaptics:enable the v1p8_gpio\n"); + gpio_direction_output(ts->v1p8_gpio, 1); + } + if (ts->enable2v8_gpio > 0) { + TPD_DEBUG("synaptics:enable the enable2v8_gpio\n"); + gpio_direction_output(ts->enable2v8_gpio, 1); + } + usleep_range(10*1000, 10*1000); + if (!IS_ERR(ts->vcc_i2c_1v8)) { + rc = regulator_enable(ts->vcc_i2c_1v8); + if (rc) + TPD_ERR("Regulator vcc_i2c enable failed rc=%d\n", rc); + } + usleep_range(10*1000, 10*1000); + if (ts->reset_gpio > 0) { + gpio_direction_output(ts->reset_gpio, 1); + usleep_range(10*1000, 10*1000); + gpio_direction_output(ts->reset_gpio, 0); + usleep_range(10*1000, 10*1000); + gpio_direction_output(ts->reset_gpio, 1); + TPD_DEBUG("synaptics:enable the reset_gpio\n"); + } + return rc; +} + +static int tpd_hw_pwroff(struct synaptics_ts_data *ts) +{ + int rc = 0; + + if (ts->reset_gpio > 0) { + TPD_DEBUG("%s set reset gpio low\n", __func__); + gpio_direction_output(ts->reset_gpio, 0); + } + + if (!IS_ERR(ts->vcc_i2c_1v8)) { + rc = regulator_disable(ts->vcc_i2c_1v8); + if (rc) { + TPD_ERR("Rvcc_i2c en fail rc=%d\n", rc); + return rc; + } + } + if (ts->v1p8_gpio > 0) { + TPD_DEBUG("snps:disable the v1p8_gpio\n"); + gpio_direction_output(ts->v1p8_gpio, 0); + } + if (!IS_ERR(ts->vdd_2v8)) { + rc = regulator_disable(ts->vdd_2v8); + if (rc) { + TPD_ERR("rvdd dis fail rc=%d\n", rc); + return rc; + } + } + if (ts->enable2v8_gpio > 0) { + TPD_DEBUG("snps:enable the enable2v8_gpio\n"); + gpio_direction_output(ts->enable2v8_gpio, 0); + } + return rc; +} + +static int tpd_power(struct synaptics_ts_data *ts, unsigned int on) +{ + int ret; + + if (on) + ret = tpd_hw_pwron(ts); + else + ret = tpd_hw_pwroff(ts); + + return ret; +} + +static int synaptics_read_register_map(struct synaptics_ts_data *ts) +{ + uint8_t buf[4]; + int ret; + + memset(buf, 0, sizeof(buf)); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("snps_read_register_map:failed for page select\n"); + return -ENOMEM; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xDD, 4, &(buf[0x0])); + if (ret < 0) { + TPD_ERR("failed for page select!\n"); + return -ENOMEM; + } + + F12_2D_QUERY_BASE = buf[0]; + F12_2D_CMD_BASE = buf[1]; + F12_2D_CTRL_BASE = buf[2]; + F12_2D_DATA_BASE = buf[3]; + + TPD_ERR("F12_2D_QUERY_BASE = %x\n" + "F12_2D_CMD_BASE = %x\n" + "F12_2D_CTRL_BASE = %x\n" + "F12_2D_DATA_BASE = %x\n", + F12_2D_QUERY_BASE, F12_2D_CMD_BASE, + F12_2D_CTRL_BASE, F12_2D_DATA_BASE); + + + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xE3, 4, &(buf[0x0])); + F01_RMI_QUERY_BASE = buf[0]; + F01_RMI_CMD_BASE = buf[1]; + F01_RMI_CTRL_BASE = buf[2]; + F01_RMI_DATA_BASE = buf[3]; + + TPD_DEBUG("F01_RMI_QUERY_BASE = %x\n" + "F01_RMI_CMD_BASE = %x\n" + "F01_RMI_CTRL_BASE = %x\n" + "F01_RMI_DATA_BASE = %x\n", + F01_RMI_QUERY_BASE, F01_RMI_CMD_BASE, + F01_RMI_CTRL_BASE, F01_RMI_DATA_BASE); + + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xE9, 4, &(buf[0x0])); + F34_FLASH_QUERY_BASE = buf[0]; + F34_FLASH_CMD_BASE = buf[1]; + F34_FLASH_CTRL_BASE = buf[2]; + F34_FLASH_DATA_BASE = buf[3]; + TPD_ERR("F34_FLASH_QUERY_BASE = %x\n" + "F34_FLASH_CMD_BASE = %x\n" + "F34_FLASH_CTRL_BASE = %x\n" + "F34_FLASH_DATA_BASE = %x\n", + F34_FLASH_QUERY_BASE, F34_FLASH_CMD_BASE, + F34_FLASH_CTRL_BASE, F34_FLASH_DATA_BASE); + + F01_RMI_QUERY11 = F01_RMI_QUERY_BASE+11; + F01_RMI_CTRL00 = F01_RMI_CTRL_BASE; + F01_RMI_CTRL01 = F01_RMI_CTRL_BASE + 1; + F01_RMI_CTRL02 = F01_RMI_CTRL_BASE + 2; + F01_RMI_CMD00 = F01_RMI_CMD_BASE; + F01_RMI_DATA01 = F01_RMI_DATA_BASE + 1; + + F12_2D_CTRL08 = F12_2D_CTRL_BASE; + F12_2D_CTRL32 = F12_2D_CTRL_BASE + 15; + F12_2D_DATA38 = F12_2D_DATA_BASE + 54; + F12_2D_DATA39 = F12_2D_DATA_BASE + 55; + F12_2D_CMD00 = F12_2D_CMD_BASE; + F12_2D_CTRL20 = F12_2D_CTRL_BASE + 0x07; + F12_2D_CTRL27 = F12_2D_CTRL_BASE + 0x0c; + + + F34_FLASH_CTRL00 = F34_FLASH_CTRL_BASE; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x4); + if (ret < 0) { + TPD_DEBUG("snps_read_register_map: failed for page select\n"); + return -ENOMEM; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xE9, 4, &(buf[0x0])); + F51_CUSTOM_QUERY_BASE = buf[0]; + F51_CUSTOM_CMD_BASE = buf[1]; + F51_CUSTOM_CTRL_BASE = buf[2]; + F51_CUSTOM_DATA_BASE = buf[3]; + F51_CUSTOM_CTRL00 = F51_CUSTOM_CTRL_BASE; + F51_CUSTOM_DATA04 = F51_CUSTOM_DATA_BASE; + F51_CUSTOM_DATA11 = F51_CUSTOM_DATA_BASE; + + TPD_ERR("F51_CUSTOM_QUERY_BASE = %x\n" + "F51_CUSTOM_CMD_BASE = %x\n" + "F51_CUSTOM_CTRL_BASE = %x\n" + "F51_CUSTOM_DATA_BASE = %x\n", + F51_CUSTOM_QUERY_BASE, F51_CUSTOM_CMD_BASE, + F51_CUSTOM_CTRL_BASE, F51_CUSTOM_DATA_BASE); + +#if TP_TEST_ENABLE + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x01); + if (ret < 0) { + TPD_ERR("snps_read_register_map: failed for page select\n"); + return -ENOMEM; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, 0xE9, 4, &(buf[0x0])); + F54_ANALOG_QUERY_BASE = buf[0]; + F54_ANALOG_COMMAND_BASE = buf[1]; + F54_ANALOG_CONTROL_BASE = buf[2]; + F54_ANALOG_DATA_BASE = buf[3]; + TPD_ERR("F54_QUERY_BASE = %x\n" + "F54_CMD_BASE = %x\n" + "F54_CTRL_BASE = %x\n" + "F54_DATA_BASE = %x\n", + F54_ANALOG_QUERY_BASE, F54_ANALOG_COMMAND_BASE, + F54_ANALOG_CONTROL_BASE, F54_ANALOG_DATA_BASE); +#endif + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + return 0; +} + +#ifdef SUPPORT_GESTURE +static int synaptics_enable_interrupt_for_gesture(struct synaptics_ts_data *ts, +int enable) +{ + int ret; + unsigned char reportbuf[4]; + + TPD_DEBUG("%s is called\n", __func__); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("%s: select page failed ret = %d\n", __func__, ret); + return -ENOMEM; + } + ret = i2c_smbus_read_i2c_block_data(ts->client, + F12_2D_CTRL20, 3, &(reportbuf[0x0])); + if (ret < 0) { + TPD_DEBUG("readreg F12_2D_CTRL20[0x%x] fail\n", F12_2D_CTRL20); + return -ENOMEM; + } + + if (enable) { + ts->in_gesture_mode = 1; + reportbuf[2] |= 0x02; + } else { + ts->in_gesture_mode = 0; + reportbuf[2] &= 0xfd; + } + TPD_DEBUG("F12_2D_CTRL20:0x%x=[2]:0x%x\n", F12_2D_CTRL20, reportbuf[2]); + ret = i2c_smbus_write_i2c_block_data(ts->client, + F12_2D_CTRL20, 3, &(reportbuf[0x0])); + if (ret < 0) { + TPD_ERR("%s :Failed to write report buffer\n", __func__); + return -ENOMEM; + } + gesture = UnkownGestrue; + return 0; +} +#endif + +#ifdef SUPPORT_GLOVES_MODE +#define GLOVES_ADDR 0x001f +static int synaptics_glove_mode_enable(struct synaptics_ts_data *ts) +{ + int ret; + + TPD_DEBUG("glove mode enable\n"); + /* page select = 0x4 */ + if (ts->glove_enable == 1) { + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_DEBUG("i2c failed for mode select\n"); + goto GLOVE_ENABLE_END; + } + ret = i2c_smbus_read_byte_data(ts->client, GLOVES_ADDR); + ret = i2c_smbus_write_byte_data(ts->client, + GLOVES_ADDR, ret | 0x01); + if (ret < 0) { + TPD_DEBUG("i2c failed for mode select\n"); + goto GLOVE_ENABLE_END; + } + } else { + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_DEBUG("i2c failed for mode select\n"); + goto GLOVE_ENABLE_END; + } + ret = i2c_smbus_read_byte_data(ts->client, GLOVES_ADDR); + ret = i2c_smbus_write_byte_data(ts->client, + GLOVES_ADDR, ret & 0xFE); + if (ret < 0) { + TPD_DEBUG("i2c failed for mode select\n"); + goto GLOVE_ENABLE_END; + } + } + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_DEBUG("i2c failed for page select\n"); + goto GLOVE_ENABLE_END; + } + +GLOVE_ENABLE_END: + return ret; +} +#endif + +#ifdef SUPPORT_TP_SLEEP_MODE +static int synaptics_sleep_mode_enable(struct synaptics_ts_data *ts) +{ + int ret; + + /* page select = 0x0 */ + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_ERR("i2c_smbus_write_byte_data failed for page select\n"); + goto SLEEP_ENABLE_END; + } + if (sleep_enable == 1) { + /*0x00:enable glove mode,0x02:disable glove mode,*/ + TPDTM_DMESG("sleep mode enable\n"); + ret = synaptics_mode_change(0x01); + if (ret < 0) { + TPD_ERR("i2c 1 failed for mode select\n"); + goto SLEEP_ENABLE_END; + } + } else { + TPDTM_DMESG("sleep mode disable\n"); + ret = synaptics_mode_change(0x84); + if (ret < 0) { + TPD_ERR("i2c 0 failed for mode select\n"); + goto SLEEP_ENABLE_END; + } + } + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_ERR("i2c_smbus_write_byte_data failed for page select\n"); + goto SLEEP_ENABLE_END; + } + +SLEEP_ENABLE_END: + return ret; +} +#endif + +static int synaptics_read_product_id(struct synaptics_ts_data *ts) +{ + uint8_t buf1[11]; + int ret; + + memset(buf1, 0, sizeof(buf1)); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPDTM_DMESG("snps_read_product_id: failed for page select\n"); + return -EINVAL; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, + F01_RMI_QUERY11, 8, &(buf1[0x0])); + ret = synaptics_rmi4_i2c_read_block(ts->client, + F01_RMI_QUERY_BASE+19, 2, &(buf1[0x8])); + if (ret < 0) { + TPD_ERR("snps_read_product_id: failed to read\n"); + return -EINVAL; + } + return 0; +} + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + TPD_DEBUG("%s is called!\n", __func__); + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("init_panel failed for page select\n"); + return -EINVAL; + } + /*device control: normal operation, configur=1*/ + + ret = synaptics_mode_change(0x80);/*change tp to doze mode*/ + if (ret < 0) { + msleep(150); + ret = synaptics_mode_change(0x80); + if (ret < 0) + TPD_ERR("%s failed for mode select\n", __func__); + } + + return ret; +} + +static int synaptics_enable_interrupt(struct synaptics_ts_data *ts, int enable) +{ + int ret; + uint8_t abs_status_int; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPDTM_DMESG("snps_enable_irq:sel page fail ret = %d\n", ret); + return -EINVAL; + } + if (enable) { + abs_status_int = 0x7f; + /*clear interrupt bits for previous touch*/ + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F01_RMI_DATA_BASE+1); + if (ret < 0) { + TPDTM_DMESG("snps_enable_irq:clear bits failed\n"); + return -ENOMEM; + } + } else { + abs_status_int = 0x0; + } + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F01_RMI_CTRL00+1, abs_status_int); + if (ret < 0) { + TPDTM_DMESG("%s:failed,abs_int =%d\n", + __func__, abs_status_int); + return -ENOMEM; + } + ret = synaptics_rmi4_i2c_read_byte(ts->client, F01_RMI_CTRL00+1); + return 0; +} + +static void delay_qt_ms(unsigned long w_ms) +{ + unsigned long i; + unsigned long j; + + for (i = 0; i < w_ms; i++) { + for (j = 0; j < 1000; j++) + udelay(1); + } +} + +static int synaptics_rmi4_i2c_read_block(struct i2c_client *client, +unsigned char addr, unsigned short length, unsigned char *data) +{ + int retval; + unsigned char retry; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + buf = addr & 0xFF; + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) { + retval = length; + break; + } + msleep(20); + } + if (retry == 2) { + dev_err(&client->dev, + "%s: I2C read over retry limit\n", + __func__); + retval = -5; + } else { + /*rst_flag_counter = 0;*/ + } + + return retval; +} + +static int synaptics_rmi4_i2c_write_block(struct i2c_client *client, +unsigned char addr, unsigned short length, unsigned char const *data) +{ + int retval; + unsigned char retry; + unsigned char buf[length + 1]; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + buf[0] = addr & 0xff; + memcpy(&buf[1], &data[0], length); + + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) { + retval = length; + break; + } + msleep(20); + } + if (retry == 2) + retval = -EIO; + + return retval; +} + +static int synaptics_rmi4_i2c_read_byte(struct i2c_client *client, +unsigned char addr) +{ + int retval = 0; + unsigned char buf[2] = {0}; + + retval = synaptics_rmi4_i2c_read_block(client, addr, 1, buf); + if (retval >= 0) + retval = buf[0] & 0xff; + return retval; +} + +static int synaptics_rmi4_i2c_write_byte(struct i2c_client *client, +unsigned char addr, unsigned char data) +{ + int retval; + unsigned char data_send = data; + + retval = synaptics_rmi4_i2c_write_block(client, addr, 1, &data_send); + return retval; +} + +static int synaptics_rmi4_i2c_read_word(struct i2c_client *client, +unsigned char addr) +{ + int retval; + unsigned char buf[2] = {0}; + + retval = synaptics_rmi4_i2c_read_block(client, addr, 2, buf); + if (retval >= 0) + retval = buf[1] << 8 | buf[0]; + return retval; +} + +static int synaptics_rmi4_i2c_write_word(struct i2c_client *client, +unsigned char addr, unsigned short data) +{ + int retval; + unsigned char buf[2] = {data & 0xff, (data >> 8) & 0xff}; + + retval = synaptics_rmi4_i2c_write_block(client, addr, 2, buf); + if (retval >= 0) + retval = buf[1] << 8 | buf[0]; + + return retval; +} + +/***************start****************/ +#ifdef SUPPORT_GESTURE +static void synaptics_get_coordinate_point(struct synaptics_ts_data *ts) +{ + int ret, i; + uint8_t coordinate_buf[25] = {0}; + uint16_t trspoint = 0; +/* add by lifeng 2016/1/19 workarounds for the gestrue two interrupts begin*/ + static uint8_t coordinate_buf_last[25] = {0}; +/* add by lifeng 2016/1/19 workarounds for the gestrue two interrupts end*/ + + TPD_DEBUG("%s is called!\n", __func__); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x4); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA11, 8, &(coordinate_buf[0])); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA11 + 8, 8, &(coordinate_buf[8])); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA11 + 16, 8, &(coordinate_buf[16])); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F51_CUSTOM_DATA11 + 24, 1, &(coordinate_buf[24])); + +/* add by lifeng 2016/1/19 workarounds for the gestrue two interrupts begin*/ + if (!memcmp(coordinate_buf_last, coordinate_buf, + sizeof(coordinate_buf))) { + TPD_ERR("%s reject the same gestrue[%d]\n", __func__, gesture); + gesture = UnkownGestrue; + } + memcpy(coordinate_buf_last, coordinate_buf, sizeof(coordinate_buf)); +/* add by lifeng 2016/1/19 workarounds for the gestrue two interrupts end*/ + + for (i = 0; i < 23; i += 2) { + trspoint = coordinate_buf[i]|coordinate_buf[i+1] << 8; + TPD_DEBUG("snps read coord_point[%d] = %d\n", i, trspoint); + } + + TPD_DEBUG("synaptics TP coordinate_buf = 0x%x\n", coordinate_buf[24]); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + Point_start.x = (coordinate_buf[0] | (coordinate_buf[1] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_start.y = (coordinate_buf[2] | (coordinate_buf[3] << 8)) + * LCD_HEIGHT / (ts->max_y); + Point_end.x = (coordinate_buf[4] | (coordinate_buf[5] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_end.y = (coordinate_buf[6] | (coordinate_buf[7] << 8)) + * LCD_HEIGHT / (ts->max_y); + Point_1st.x = (coordinate_buf[8] | (coordinate_buf[9] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_1st.y = (coordinate_buf[10] | (coordinate_buf[11] << 8)) + * LCD_HEIGHT / (ts->max_y); + Point_2nd.x = (coordinate_buf[12] | (coordinate_buf[13] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_2nd.y = (coordinate_buf[14] | (coordinate_buf[15] << 8)) + * LCD_HEIGHT / (ts->max_y); + Point_3rd.x = (coordinate_buf[16] | (coordinate_buf[17] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_3rd.y = (coordinate_buf[18] | (coordinate_buf[19] << 8)) + * LCD_HEIGHT / (ts->max_y); + Point_4th.x = (coordinate_buf[20] | (coordinate_buf[21] << 8)) + * LCD_WIDTH / (ts->max_x); + Point_4th.y = (coordinate_buf[22] | (coordinate_buf[23] << 8)) + * LCD_HEIGHT / (ts->max_y); + clockwise = (coordinate_buf[24] & 0x10) ? 1 : + (coordinate_buf[24] & 0x20) ? 0 : 2; +} + +static void gesture_judge(struct synaptics_ts_data *ts) +{ + unsigned int keyCode = KEY_F4; + int ret = 0, gesture_sign, regswipe; + uint8_t gesture_buffer[10]; + unsigned char reportbuf[3]; + + if (version_is_s3508) + F12_2D_DATA04 = 0x0008; + else + F12_2D_DATA04 = 0x000A; + + TPD_DEBUG("%s start!\n", __func__); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + TPDTM_DMESG("failed to transfer the data, ret = %d\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + ret = i2c_smbus_read_i2c_block_data(ts->client, + F12_2D_DATA04, 5, &(gesture_buffer[0])); + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x4); + + regswipe = i2c_smbus_read_byte_data(ts->client, + F51_CUSTOM_DATA04+0x18); + + TPD_DEBUG("Gesture [0x%x]=[0x%x],lpwg Swipe ID[0x4%x] = [0x%x]\n", + F12_2D_DATA04, gesture_buffer[0], (F51_CUSTOM_DATA04+0x18), regswipe); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + gesture_sign = gesture_buffer[0]; + + switch (gesture_sign) { + case DTAP_DETECT: + gesture = DouTap; + break; + case SWIPE_DETECT: + if (version_is_s3508) { + gesture = (regswipe == 0x41) ? Left2RightSwip : + (regswipe == 0x42) ? Right2LeftSwip : + (regswipe == 0x44) ? Up2DownSwip : + (regswipe == 0x48) ? Down2UpSwip : + (regswipe == 0x80) ? DouSwip : UnkownGestrue; + } else { + gesture = (regswipe == 0x41) ? Left2RightSwip : + (regswipe == 0x42) ? Right2LeftSwip : + (regswipe == 0x44) ? Up2DownSwip : + (regswipe == 0x48) ? Down2UpSwip : + (regswipe == 0x84) ? DouSwip : UnkownGestrue; + } + break; + case CIRCLE_DETECT: + gesture = Circle; + break; + case VEE_DETECT: + gesture = (gesture_buffer[2] == 0x01) ? DownVee : + (gesture_buffer[2] == 0x02) ? UpVee : + (gesture_buffer[2] == 0x04) ? RightVee : + (gesture_buffer[2] == 0x08) ? LeftVee : UnkownGestrue; + break; + case UNICODE_DETECT: + gesture = (gesture_buffer[2] == 0x77) ? Wgestrue : + (gesture_buffer[2] == 0x6d) ? Mgestrue : + (gesture_buffer[2] == 0x73) ? Sgestrue : UnkownGestrue; + } + + keyCode = UnkownGestrue; + /* Get key code based on registered gesture */ + switch (gesture) { + case DouTap: + keyCode = KEY_DOUBLE_TAP; + break; + case UpVee: + keyCode = KEY_GESTURE_V; + break; + case DownVee: + keyCode = KEY_GESTURE_V; + break; + case LeftVee: + keyCode = KEY_GESTURE_RIGHT_V; + break; + case RightVee: + keyCode = KEY_GESTURE_LEFT_V; + break; + case Circle: + keyCode = KEY_GESTURE_CIRCLE; + break; + case DouSwip: + keyCode = KEY_GESTURE_TWO_SWIPE; + break; + case Wgestrue: + keyCode = KEY_GESTURE_W; + break; + case Mgestrue: + keyCode = KEY_GESTURE_M; + break; + case Sgestrue: + keyCode = KEY_GESTURE_S; + break; + default: + break; + } + + TPD_ERR("detect %s gesture\n", gesture == DouTap ? "(double tap)" : + gesture == UpVee ? "(V)" : + gesture == DownVee ? "(^)" : + gesture == LeftVee ? "(>)" : + gesture == RightVee ? "(<)" : + gesture == Circle ? "(O)" : + gesture == DouSwip ? "(||)" : + gesture == Left2RightSwip ? "(-->)" : + gesture == Right2LeftSwip ? "(<--)" : + gesture == Up2DownSwip ? "(up to down |)" : + gesture == Down2UpSwip ? "(down to up |)" : + gesture == Mgestrue ? "(M)" : + gesture == Sgestrue ? "(S)" : + gesture == Wgestrue ? "(W)" : "[unknown]"); + synaptics_get_coordinate_point(ts); + + TPD_DEBUG("gesture support LeftVee:%d RightVee:%d DouSwip:%d\n" + "Circle:%d UpVee:%d DouTap:%d\n", LeftVee_gesture, RightVee_gesture, + DouSwip_gesture, Circle_gesture, UpVee_gesture, DouTap_gesture); + + if ((gesture == DouTap && DouTap_gesture) + || (gesture == RightVee && RightVee_gesture) + || (gesture == LeftVee && LeftVee_gesture) + || (gesture == UpVee && UpVee_gesture) + || (gesture == Circle && Circle_gesture) + || (gesture == DouSwip && DouSwip_gesture) + || gesture == Sgestrue || gesture == Wgestrue || gesture == Mgestrue) { + gesture_upload = gesture; + input_report_key(ts->input_dev, keyCode, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, keyCode, 0); + input_sync(ts->input_dev); + } else { + ret = i2c_smbus_read_i2c_block_data(ts->client, + F12_2D_CTRL20, 3, &(reportbuf[0x0])); + ret = reportbuf[2] & 0x20; + if (ret == 0) + reportbuf[2] |= 0x02; + ret = i2c_smbus_write_i2c_block_data(ts->client, + F12_2D_CTRL20, 3, &(reportbuf[0x0])); + if (ret < 0) { + TPD_ERR("%s :Failed to write buffer\n", __func__); + return; + } + } + TPD_DEBUG("%s end!\n", __func__); +} +#endif +/***************end****************/ +static char prlog_count; +#ifdef REPORT_2D_PRESSURE +static unsigned char pres_value; +#endif +#ifdef SUPPORT_VIRTUAL_KEY +bool key_back_pressed; +bool key_appselect_pressed; +bool key_home_pressed; +#endif +void int_touch(void) +{ + int ret = -1, i = 0; + uint8_t buf[90]; + uint8_t count_data = 0; + uint8_t object_attention[2]; + uint16_t total_status = 0; + uint8_t finger_num = 0; + uint8_t finger_status = 0; + struct point_info points; + uint32_t finger_info = 0; + static uint8_t current_status; + uint8_t last_status = 0; +#ifdef SUPPORT_VIRTUAL_KEY + bool key_appselect_check = false; + bool key_back_check = false; + bool key_home_check = false; + bool key_pressed = key_appselect_pressed || key_back_pressed; +#endif + struct synaptics_ts_data *ts = ts_g; + + memset(buf, 0, sizeof(buf)); + points.x = 0; + points.y = 0; + points.z = 0; + points.status = 0; + + mutex_lock(&ts->mutexreport); +#ifdef REPORT_2D_PRESSURE + if (ts->support_ft) { + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x4); + ret = synaptics_rmi4_i2c_read_block(ts->client, 0x19, + sizeof(points.pressure), &points.pressure); + + if (ret < 0) { + TPD_ERR("synaptics_int_touch: i2c_transfer failed\n"); + goto INT_TOUCH_END; + } + if (points.pressure == 0) { + pres_value++; + if (pres_value == 255) + pres_value = 1; + } else { + pres_value = points.pressure; + } + } +#endif + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + if (version_is_s3508) + F12_2D_DATA15 = 0x0009; + else + F12_2D_DATA15 = 0x000C; + ret = synaptics_rmi4_i2c_read_block(ts->client, + F12_2D_DATA15, 2, object_attention); + + if (ret < 0) { + TPD_ERR("snps F12_2D_DATA15: i2c_transfer failed\n"); + goto INT_TOUCH_END; + } + total_status = (object_attention[1] << 8) | object_attention[0]; + + if (total_status) { + while (total_status) { + count_data++; + total_status >>= 1; + } + } else { + count_data = 0; + } + if (count_data > 10) { + TPD_ERR("count_data is: %d\n", count_data); + goto INT_TOUCH_END; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, + F12_2D_DATA_BASE, count_data*8 + 1, buf); + if (ret < 0) { + TPD_ERR("snps F12_2D_DATA_BASE: i2c_transfer failed\n"); + goto INT_TOUCH_END; + } + for (i = 0; i < count_data; i++) { + points.status = buf[i*8]; + points.x = ((buf[i*8+2]&0x0f)<<8) | (buf[i*8+1] & 0xff); + points.raw_x = buf[i*8+6] & 0x0f; + points.y = ((buf[i*8+4]&0x0f)<<8) | (buf[i*8+3] & 0xff); + points.raw_y = buf[i*8+7] & 0x0f; + points.z = buf[i*8+5]; + finger_info <<= 1; + finger_status = points.status & 0x03; +#ifdef SUPPORT_VIRTUAL_KEY + if (virtual_key_enable) { + if (points.y > 0x780 && key_pressed) { + TPD_DEBUG("Drop TP event due to key pressed\n"); + finger_status = 0; + } else { + finger_status = points.status & 0x03; + } + } else { + finger_status = points.status & 0x03; + } + if (virtual_key_enable) { + if (!finger_status) { + if (key_appselect_pressed + && !key_appselect_check) { + points.x = 0xb4; + points.y = 0x7e2; + points.z = 0x33; + points.raw_x = 4; + points.raw_y = 6; + key_appselect_check = true; + points.status = 1; + finger_status = points.status & 0x03; + } else if (key_back_pressed + && !key_back_check) { + points.x = 0x384; + points.y = 0x7e2; + points.z = 0x33; + points.raw_x = 4; + points.raw_y = 6; + key_back_check = true; + points.status = 1; + finger_status = points.status & 0x03; + } else if (key_home_pressed + && !key_home_check) { + points.x = 0x21c; + points.y = 0x7e2; + points.z = 0x33; + points.raw_x = 4; + points.raw_y = 6; + key_home_check = true; + points.status = 1; + finger_status = points.status & 0x03; + } + } + } +#endif + if (version_is_s3508 == 0) { + points.x = 1079 - points.x; + points.y = 1919 - points.y; + } + if (finger_status) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, finger_status); + input_report_key(ts->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, + ABS_MT_POSITION_X, points.x); + input_report_abs(ts->input_dev, + ABS_MT_POSITION_Y, points.y); + input_report_abs(ts->input_dev, + ABS_MT_TOUCH_MAJOR, max(points.raw_x, points.raw_y)); + input_report_abs(ts->input_dev, + ABS_MT_TOUCH_MINOR, min(points.raw_x, points.raw_y)); +#ifdef REPORT_2D_PRESSURE + if (ts->support_ft) { + input_report_abs(ts->input_dev, + ABS_MT_PRESSURE, pres_value); + TPD_DEBUG("%s: pressure%d[%d]\n", + __func__, i, pres_value); + } +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(ts->input_dev); +#endif +#ifdef SUPPORT_VIRTUAL_KEY + if (virtual_key_enable) + /*complete(&key_cm);*/ +#endif + finger_num++; + finger_info |= 1; + } + } + finger_info <<= (ts->max_num - count_data); + + for (i = 0; i < ts->max_num; i++) { + finger_status = (finger_info >> (ts->max_num-i-1)) & 1; + if (!finger_status) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, finger_status); + } + + } + + last_status = current_status & 0x02; + + if (finger_num == 0/* && last_status && (check_key <= 1)*/) { + if (3 == (++prlog_count % 6)) + TPD_ERR("all finger up\n"); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(ts->input_dev); +#endif + } + input_sync(ts->input_dev); + + if ((finger_num == 0) && (get_tp_base == 0)) {/*get base once*/ + get_tp_base = 1; + TPD_ERR("start get base data:%d\n", get_tp_base); + tp_baseline_get(ts, false); + } + +#ifdef SUPPORT_GESTURE + if (ts->in_gesture_mode == 1 && ts->is_suspended == 1) + gesture_judge(ts); +#endif +INT_TOUCH_END: + mutex_unlock(&ts->mutexreport); +} +static char log_count; +#ifdef SUPPORT_TP_TOUCHKEY +#define OEM_KEY_BACK (key_switch ? KEY_APPSELECT : KEY_BACK) +#define OEM_KEY_APPSELECT (key_switch ? KEY_BACK : KEY_APPSELECT) +#else +#define OEM_KEY_BACK KEY_BACK +#define OEM_KEY_APPSELECT KEY_APPSELECT +#endif +static void int_key_report_s3508(struct synaptics_ts_data *ts) +{ + int ret = 0; + int F1A_0D_DATA00 = 0x00; + int button_key; + + if (ts->is_suspended == 1) + return; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x02); + if (ret < 0) { + TPD_ERR("%s: line[%d]Failed to change page!!\n", + __func__, __LINE__); + return; + } + button_key = synaptics_rmi4_i2c_read_byte(ts->client, F1A_0D_DATA00); + if ((++log_count % 4) == 1) + TPD_ERR("touch_key[0x%x],touchkey_state[0x%x]\n", + button_key, ts->pre_btn_state); + if ((button_key & 0x01) && !(ts->pre_btn_state & 0x01) + && !key_back_disable) { + input_report_key(ts->input_dev, OEM_KEY_BACK, 1); + input_sync(ts->input_dev); + } else if (!(button_key & 0x01) && (ts->pre_btn_state & 0x01) + && !key_back_disable){ + input_report_key(ts->input_dev, OEM_KEY_BACK, 0); + input_sync(ts->input_dev); + } + + if ((button_key & 0x02) && !(ts->pre_btn_state & 0x02) + && !key_appselect_disable) { + input_report_key(ts->input_dev, OEM_KEY_APPSELECT, 1); + input_sync(ts->input_dev); + } else if (!(button_key & 0x02) && (ts->pre_btn_state & 0x02) + && !key_appselect_disable) { + input_report_key(ts->input_dev, OEM_KEY_APPSELECT, 0); + input_sync(ts->input_dev); + } + + ts->pre_btn_state = button_key & 0x07; + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_ERR("%s: line[%d]Failed to change page!!\n", + __func__, __LINE__); + return; + } +} + +static int synaptics_rmi4_free_fingers(struct synaptics_ts_data *ts) +{ + unsigned char i; + +#ifdef TYPE_B_PROTOCOL + for (i = 0; i < ts->max_num; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + return 0; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int ret, status_check; + uint8_t status = 0; + uint8_t inte = 0; + + struct synaptics_ts_data *ts = ts_g; + + if (atomic_read(&ts->is_stop) == 1) { + touch_disable(ts); + return; + } + + if (ts->enable_remote) + goto END; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + ret = synaptics_rmi4_i2c_read_word(ts->client, F01_RMI_DATA_BASE); + + if (ret < 0) { + TPDTM_DMESG("Synaptic:ret = %d\n", ret); + synaptics_hard_reset(ts); + goto END; + } + status = ret & 0xff; + inte = (ret & 0x7f00) >> 8; + + if (status & 0x80) { + TPD_DEBUG("enter reset,mode = %d\n", ts->in_gesture_mode); + status_check = synaptics_init_panel(ts); + if (status_check < 0) + TPD_ERR("synaptics_init_panel failed\n"); + + if ((ts->is_suspended == 1) && (ts->gesture_enable == 1)) + synaptics_enable_interrupt_for_gesture(ts, 1); + } + + if (inte == 1) { + TPD_ERR("%s: spontaneous reset detected\n", __func__); + ret = synaptics_rmi4_free_fingers(ts); + if (ret < 0) + TPD_ERR("%s: Failed to reinit device\n", __func__); + } + + if (inte & 0x04) + int_touch(); + + if (inte & 0x10) + int_key_report_s3508(ts); + +END: + touch_enable(ts); +} + +#ifndef TPD_USE_EINT +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = + container_of(timer, struct synaptics_ts_data, timer); + + mutex_lock(&ts->mutex); + synaptics_ts_work_func(ts); + mutex_unlock(&ts->mutex); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} +#else +static irqreturn_t synaptics_irq_thread_fn(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = (struct synaptics_ts_data *)dev_id; + + touch_disable(ts); + synaptics_ts_work_func(&ts->report_work); + + return IRQ_HANDLED; +} +#endif + +static ssize_t tp_baseline_test_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return baseline_ret; + if (baseline_ret == 0) { + count = synaptics_rmi4_baseline_show(ts->dev, page, 1); + baseline_ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + } else { + baseline_ret = 0; + } + return baseline_ret; +} + +static ssize_t i2c_device_test_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts_g) + return ret; + TPD_DEBUG("gesture enable is: %d\n", ts->gesture_enable); + ret = snprintf(page, 4, "%d\n", ts->i2c_device_test); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +#ifdef SUPPORT_GESTURE +static ssize_t tp_gesture_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + TPD_DEBUG("gesture enable is: %d\n", ts->gesture_enable); + ret = snprintf(page, 4, "%d\n", ts->gesture_enable); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static ssize_t tp_gesture_write_func(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[10]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return count; + if (count > 3 || ts->is_suspended) + return count; + if (copy_from_user(buf, buffer, count)) { + TPD_ERR(KERN_INFO "%s: read proc input error.\n", __func__); + return count; + } + TPD_ERR("%s write argc1[0x%x],argc2[0x%x]\n", + __func__, buf[0], buf[1]); + + UpVee_gesture = (buf[0] & BIT0)?1:0; /*"V"*/ + DouSwip_gesture = (buf[0] & BIT1)?1:0;/*"||"*/ + LeftVee_gesture = (buf[0] & BIT3)?1:0; /*">"*/ + RightVee_gesture = (buf[0] & BIT4)?1:0;/*"<"*/ + Circle_gesture = (buf[0] & BIT6)?1:0; /*"O"*/ + DouTap_gesture = (buf[0] & BIT7)?1:0; /*double tap*/ + + Sgestrue_gesture = (buf[1] & BIT0)?1:0;/*"S"*/ + Mgestrue_gesture = (buf[1] & BIT1)?1:0; /*"M"*/ + Wgestrue_gesture = (buf[1] & BIT2)?1:0; /*"W"*/ + + if (DouTap_gesture || Circle_gesture || UpVee_gesture || + LeftVee_gesture || RightVee_gesture || DouSwip_gesture || + Sgestrue_gesture || Mgestrue_gesture || Wgestrue_gesture) { + ts->gesture_enable = 1; + } else { + ts->gesture_enable = 0; + } + + return count; +} +static ssize_t coordinate_proc_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + + TPD_ERR("%s:gesture_upload = %d\n", __func__, gesture_upload); + ret = snprintf(page, 64, + "%d,%d:%d,%d:%d,%d:%d,%d:%d,%d:%d,%d:%d,%d\n", + gesture_upload, Point_start.x, Point_start.y, Point_end.x, Point_end.y, + Point_1st.x, Point_1st.y, Point_2nd.x, Point_2nd.y, Point_3rd.x, + Point_3rd.y, Point_4th.x, Point_4th.y, clockwise); + + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + + return ret; +} + +static ssize_t gesture_switch_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + ret = snprintf(page, 18, "gesture_switch:%d\n", gesture_switch); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static ssize_t gesture_switch_write_func(struct file *file, +const char __user *page, size_t count, loff_t *ppos) +{ + int ret, write_flag = 0; + char buf[10] = {0}; + struct synaptics_ts_data *ts = ts_g; + + if (ts->loading_fw) { + TPD_ERR("%s FW is updating break!!\n", __func__); + return count; + } + if (copy_from_user(buf, page, count)) { + TPD_ERR("%s: read proc input error.\n", __func__); + return count; + } + ret = kstrtoint(buf, 10, &write_flag); + gesture_switch = write_flag; + TPD_ERR("gesture_switch:%d,suspend:%d,gesture:%d\n", gesture_switch, + ts->is_suspended, ts->gesture_enable); + if (gesture_switch == 1) { + if ((ts->is_suspended == 1) && (ts->gesture_enable == 1)) { + i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + synaptics_mode_change(0x80); + /*touch_enable(ts);*/ + synaptics_enable_interrupt_for_gesture(ts, 1); + } + } else if (gesture_switch == 2) { + if ((ts->is_suspended == 1) && (ts->gesture_enable == 1)) { + i2c_smbus_write_byte_data(ts->client, 0xff, 0x0); + synaptics_mode_change(0x81); + /*touch_enable(ts);*/ + synaptics_enable_interrupt_for_gesture(ts, 0); + } + } + + return count; +} + +/******************************start****************************/ +static const struct file_operations tp_gesture_proc_fops = { + .write = tp_gesture_write_func, + .read = tp_gesture_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static const struct file_operations gesture_switch_proc_fops = { + .write = gesture_switch_write_func, + .read = gesture_switch_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static const struct file_operations coordinate_proc_fops = { + .read = coordinate_proc_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +#endif +static int page, address, block; +static ssize_t synap_read_address(struct file *file, char __user *user_buf, +size_t count, loff_t *ppos) +{ + int ret; + char buffer[PAGESIZE]; + char buf[128]; + int i; + int cnt = 0; + struct synaptics_ts_data *ts = ts_g; + + TPD_DEBUG("%s page=0x%x,address=0x%x,block=0x%x\n", + __func__, page, address, block); + + cnt += snprintf(&(buffer[cnt]), 32, "page=0x%x,add=0x%x,block=0x%x\n", + page, address, block); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, page); + ret = synaptics_rmi4_i2c_read_block(ts->client, address, block, buf); + for (i = 0; i < block; i++) { + cnt += snprintf(&(buffer[cnt]), + 16, "buf[%d]=0x%x\n", i, buf[i]); + TPD_DEBUG("buffer[%d]=0x%x\n", i, buffer[i]); + } + ret = simple_read_from_buffer(user_buf, count, + ppos, buffer, strlen(buffer)); + + return ret; +} + +static ssize_t synap_write_address(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + int buf[128]; + int ret, i; + struct synaptics_ts_data *ts = ts_g; + int temp_block, wbyte; + char reg[30]; + + ret = + sscanf(buffer, "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", + &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5], &buf[6], + &buf[7], &buf[8], &buf[9], &buf[10], &buf[11], &buf[12], &buf[13], + &buf[14], &buf[15], &buf[16], &buf[17]); + + for (i = 0; i < ret; i++) + TPD_DEBUG("buf[i]=0x%x,", buf[i]); + + TPD_DEBUG("\n"); + page = buf[0]; + address = buf[1]; + temp_block = buf[2]; + wbyte = buf[3]; + + if (temp_block == 0xFF) { + for (i = 0; i < wbyte; i++) + reg[i] = (char)buf[4+i]; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, page); + ret = synaptics_rmi4_i2c_write_block(ts->client, + (char)address, wbyte, reg); + TPD_DEBUG("%s write page=0x%x,address=0x%x\n", + __func__, page, address); + for (i = 0; i < wbyte; i++) + TPD_DEBUG("reg=0x%x\n", reg[i]); + } else + block = temp_block; + return count; +} + +#ifdef SUPPORT_GLOVES_MODE +static ssize_t tp_glove_read_func(struct file *file, char __user *user_buf, +size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + TPD_DEBUG("glove mode enable is: %d\n", ts->glove_enable); + ret = snprintf(page, 4, "%d\n", ts->glove_enable); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static ssize_t tp_glove_write_func(struct file *file, const char __user *buffer, +size_t count, loff_t *ppos) +{ + struct synaptics_ts_data *ts = ts_g; + int ret = 0; + char buf[10] = {0}; + int rc; + + if (count > 10) + goto GLOVE_ENABLE_END; + if (copy_from_user(buf, buffer, count)) { + TPD_ERR("%s: read proc input error.\n", __func__); + goto GLOVE_ENABLE_END; + } + rc = kstrtoint(buf, 10, &ret); + if (rc < 0) + return rc; + if (!ts) + return count; + TPDTM_DMESG("tp_glove_write_func:buf = %d,ret = %d\n", *buf, ret); + if ((ret == 0) || (ret == 1)) { + ts->glove_enable = ret; + synaptics_glove_mode_enable(ts); + } + switch (ret) { + case 0: + TPDTM_DMESG("tp_glove_func will be disable\n"); + break; + case 1: + TPDTM_DMESG("tp_glove_func will be enable\n"); + break; + default: + TPDTM_DMESG("Pls enter 0 or 1 to ctrl glove func\n"); + } +GLOVE_ENABLE_END: + return count; +} +#endif + + +#ifdef SUPPORT_TP_SLEEP_MODE +static ssize_t tp_sleep_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + + TPD_DEBUG("sleep mode enable is: %d\n", sleep_enable); + ret = snprintf(page, 4, "%d\n", sleep_enable); + ret = simple_read_from_buffer(user_buf, count + , ppos, page, strlen(page)); + return ret; +} + +static ssize_t tp_sleep_write_func(struct file *file, +const char *buffer, size_t count, loff_t *ppos) +{ + char buf[10] = {0}; + struct synaptics_ts_data *ts = ts_g; + int ret = 0; + int rc; + + if (count > 10) + return count; + if (!ts) + return count; + if (copy_from_user(buf, buffer, count)) { + TPD_ERR(KERN_INFO "%s: read proc input error.\n", __func__); + return count; + } + + rc = kstrtoint(buf, 10, &ret); + TPDTM_DMESG("tp_sleep_write_func:buf = %d,ret = %d\n", *buf, ret); + + if ((ret == 0) || (ret == 1)) { + sleep_enable = ret; + synaptics_sleep_mode_enable(ts); + } + + switch (ret) { + case 0: + TPDTM_DMESG("tp_sleep_func will be disable\n"); + break; + case 1: + TPDTM_DMESG("tp_sleep_func will be enable\n"); + break; + default: + TPDTM_DMESG("pls enter 0 or 1 to ctrl sleep func\n"); + } + return count; +} +#endif + +static ssize_t tp_show(struct device_driver *ddri, char *buf) +{ + struct synaptics_ts_data *ts = ts_g; + int a; + int b, c; + + if (!ts) + return 0; + a = synaptics_rmi4_i2c_read_word(ts->client, F01_RMI_DATA_BASE); + if (a < 0) + TPD_ERR("tp_show read i2c err\n"); + b = synaptics_rmi4_i2c_read_byte(ts->client, F01_RMI_DATA01); + if (b < 0) + TPD_ERR("tp_show read i2c err\n"); + c = synaptics_rmi4_i2c_read_byte(ts->client, F12_2D_DATA_BASE); + if (c < 0) + TPD_ERR("tp_show read i2c err\n"); + + return snprintf(buf, 88, "F01_RMI_DATA_BASE[0x%x]=0x%x;\n" + "F01_RMI_DATA01[0x%x]=0x%x;F12_2D_DATA_BASE[0x%x]=0x%x;\n", + F01_RMI_DATA_BASE, a, F01_RMI_DATA01, b, F12_2D_DATA_BASE, c); +} + +static ssize_t store_tp(struct device_driver *ddri, +const char *buf, size_t count) +{ + int tmp = 0; + int ret; + + ret = kstrtoint(buf, 10, &tmp); + if (ret >= 0) { + tp_debug = tmp; + } else { + TPDTM_DMESG("invalid content: '%s', length = %zd\n", + buf, count); + } + return count; +} +static ssize_t vendor_id_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[4]; + + ret = snprintf(page, 3, "%d\n", 7); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +#if TP_TEST_ENABLE +static int synaptics_read_register_map_page1(struct synaptics_ts_data *ts) +{ + unsigned char buf[4]; + int ret; + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x1); + if (ret < 0) { + TPD_ERR("snps_rmi4_i2c_write_byte failed for page select\n"); + return -ENOMEM; + } + ret = synaptics_rmi4_i2c_read_block(ts->client, + 0xE9, 4, &(buf[0x0])); + F54_ANALOG_QUERY_BASE = buf[0]; + F54_ANALOG_COMMAND_BASE = buf[1]; + F54_ANALOG_CONTROL_BASE = buf[2]; + F54_ANALOG_DATA_BASE = buf[3]; + + TPD_ERR("F54_ANALOG_QUERY_BASE = 0x%x\n" + "F54_ANALOG_COMMAND_BASE = 0x%x\n" + "F54_ANALOG_CONTROL_BASE = 0x%x\n" + "F54_ANALOG_DATA_BASE = 0x%x\n", + F54_ANALOG_QUERY_BASE, F54_ANALOG_COMMAND_BASE, + F54_ANALOG_CONTROL_BASE, F54_ANALOG_DATA_BASE); + return 0; +} + +static void checkCMD(int delay_time) +{ + int ret; + int flag_err = 0; + struct synaptics_ts_data *ts = ts_g; + + do { + delay_qt_ms(delay_time); /*wait delay_time ms*/ + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_COMMAND_BASE); + flag_err++; + } while ((ret > 0x00) && (flag_err < 30)); + if (ret > 0x00 || flag_err >= 30) + TPD_ERR("checkCMD error ret is %x flag_err is %d\n", + ret, flag_err); +} +static void checkCMD_RT133(void) +{ + int ret = 0; + int err_count = 0; + struct synaptics_ts_data *ts = ts_g; + + do { + delay_qt_ms(10); + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_COMMAND_BASE); + err_count++; + } while ((ret & 0x01) && (err_count < 30)); + if (ret & 0x01 || err_count >= 30) + TPD_ERR("%s line%d %x count %d\n", + __func__, __LINE__, ret, err_count); +} + +#endif + +static ssize_t tp_baseline_show(struct device_driver *ddri, char *buf) +{ + int ret = 0; + int x, y; + ssize_t num_read_chars = 0; + uint8_t tmp_l = 0, tmp_h = 0; + uint16_t tmp_old = 0; + uint16_t tmp_new = 0; + uint16_t count = 0; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return count; + memset(delta_baseline, 0, sizeof(delta_baseline)); + /*disable irq when read data from IC*/ + touch_disable(ts); + mutex_lock(&ts->mutex); + synaptics_read_register_map_page1(ts); + + TPD_DEBUG("\nstep 1:select report type 0x03 baseline\n"); + + /*step 1:check raw capacitance*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x03);/*select report type 0x03*/ + if (ret < 0) { + TPD_ERR("step 1: select report type 0x03 failed\n"); + /*return sprintf(buf, "i2c err!");*/ + } + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+20, 0x01); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23); + tmp_old = ret & 0xff; + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+23, (tmp_old & 0xef)); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+27); + tmp_new = ret & 0xdf; + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+27, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); /*force update*/ + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE+7, 0x01);/*Forbid NoiseMitigation*/ + + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); /*force update*/ + checkCMD(10); + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02);/*force Cal*/ + checkCMD(10); + + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE+1, 0x00);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(10); + count = 0; + for (x = 0; x < TX_NUM; x++) { + num_read_chars += snprintf(&(buf[num_read_chars]), + 5, "[%d]", x); + for (y = 0; y < RX_NUM; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + delta_baseline[x][y] = (tmp_h << 8) | tmp_l; + num_read_chars += snprintf(&(buf[num_read_chars]), + 8, "%5d", delta_baseline[x][y]); + } + num_read_chars += snprintf(&(buf[num_read_chars]), 2, "\n"); + } + num_read_chars += snprintf(&(buf[num_read_chars]), 2, "\n"); + TPD_DEBUG("\nread all is oK\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02); + delay_qt_ms(60); + +#ifdef SUPPORT_GLOVES_MODE + synaptics_glove_mode_enable(ts); +#endif + synaptics_init_panel(ts); + + synaptics_enable_interrupt(ts, 1); + ret = synaptics_soft_reset(ts); + if (ret < 0) + TPD_ERR("%s faile to reset device\n", __func__); + + mutex_unlock(&ts->mutex); + + return num_read_chars; + +} + +static ssize_t tp_rawdata_show(struct device_driver *ddri, char *buf) +{ + int ret = 0; + int x, y; + ssize_t num_read_chars = 0; + uint8_t tmp_l = 0, tmp_h = 0; + uint16_t count = 0; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return 0; + memset(delta_baseline, 0, sizeof(delta_baseline)); + /*disable irq when read data from IC*/ + touch_disable(ts); + mutex_lock(&ts->mutex); + synaptics_read_register_map_page1(ts); + + memset(delta_baseline, 0, sizeof(delta_baseline)); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_DATA_BASE, 0x02);/*select report type 0x02*/ + ret = synaptics_rmi4_i2c_write_word(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x00);/*set fifo 00*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0X01);/*get report*/ + checkCMD(10); + count = 0; + for (x = 0; x < TX_NUM; x++) { + num_read_chars += snprintf(&(buf[num_read_chars]), + 6, "\n[%d]", x); + for (y = 0; y < RX_NUM; y++) { + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + delta_baseline[x][y] = (tmp_h<<8) | tmp_l; + num_read_chars += snprintf(&(buf[num_read_chars]), + 8, "%3d ", delta_baseline[x][y]); + } + } + num_read_chars += snprintf(&(buf[num_read_chars]), 2, "\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02); + delay_qt_ms(60); + synaptics_enable_interrupt(ts, 1); + mutex_unlock(&ts->mutex); + touch_enable(ts); + return num_read_chars; +} + +static ssize_t tp_delta_store(struct device_driver *ddri, + const char *buf, size_t count) +{ + TPDTM_DMESG("tp_test_store is not support\n"); + return count; +} + +static ssize_t synaptics_rmi4_baseline_show_s3508(struct device *dev, +char *buf, bool savefile) +{ + + ssize_t num_read_chars = 0; +#if TP_TEST_ENABLE + int ret = 0; + uint8_t x, y; + int tx_datal; + int16_t err_RT251 = 0, err_RT251_self = 0, err_RT253 = 0; + int16_t baseline_data = 0; + uint16_t unsigned_baseline_data = 0; + uint8_t tmp_old = 0; + uint8_t tmp_new = 0; + uint8_t tmp_l = 0, tmp_h = 0; + uint16_t count = 0; + int error_count = 0; + uint8_t buffer[9] = {0}; + int16_t *baseline_data_test; + int enable_cbc = 0; + int readdata_fail = 0, first_check = 0; + int16_t left_ramdata = 0, right_ramdata = 0; + struct timespec now_time; + struct rtc_time rtc_now_time; + uint8_t data_buf[64]; + uint32_t CURRENT_FIRMWARE_ID = 0; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + synaptics_rmi4_i2c_read_block(ts->client, + F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0]<<24) + | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + TPD_ERR("[sk]CURRENT_FIRMWARE_ID = 0x%x\n", CURRENT_FIRMWARE_ID); + snprintf(ts->fw_id, 12, "0x%x", CURRENT_FIRMWARE_ID); + + push_component_info(TP, ts->fw_id, ts->manu_name); +READDATA_AGAIN: + msleep(30); + mutex_lock(&ts->mutex); + touch_disable(ts); + + memset(Rxdata, 0, sizeof(Rxdata)); + synaptics_read_register_map_page1(ts); + + if (savefile) { + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(data_buf, 40, + "/sdcard/tp_testlimit_%02d%02d%02d-%02d%02d%02d.csv", + (rtc_now_time.tm_year + 1900)%100, + rtc_now_time.tm_mon + 1, rtc_now_time.tm_mday, + rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); + + } + + /*step 1:check raw capacitance*/ +TEST_WITH_CBC_s3508: + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x03);/*select report type 0x03*/ + if (ret < 0) { + TPD_ERR("read_baseline: i2c_smbus_write_byte_data failed\n"); + goto END; + } + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 20, 0x01); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 23); + tmp_old = ret & 0xff; + + if (enable_cbc) { + TPD_DEBUG("ret = %x ,tmp_old =%x ,tmp_new = %x\n", + ret, tmp_old, (tmp_old | 0x10)); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 23, (tmp_old | 0x10)); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + checkCMD(30); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27); + tmp_new = ret | 0x20; + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + TPD_DEBUG("Test open cbc\n"); + if (CURRENT_FIRMWARE_ID == 0xAB056006) + baseline_data_test = + (int16_t *)baseline_cap_data_old[0]; + else { + if (ts->support_1080x2160_tp) + baseline_data_test = + (int16_t *)baseline_cap_17801_data[0]; + else + baseline_data_test = + (int16_t *)baseline_cap_data[0]; + } + } else { + TPD_DEBUG("ret = %x ,tmp_old =%x ,tmp_new = %x\n", + ret, tmp_old, (tmp_old & 0xef)); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 23, (tmp_old & 0xef)); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27); + tmp_new = ret & 0xdf; + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); /*force update*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 7, 0x01);/*Forbid NoiseMitigation*/ + if (CURRENT_FIRMWARE_ID == 0xAB056006) + baseline_data_test = + (int16_t *)baseline_cap_data_old[1]; + else { + if (ts->support_1080x2160_tp) + baseline_data_test = + (int16_t *)baseline_cap_17801_data[1]; + else + baseline_data_test = + (int16_t *)baseline_cap_data[1]; + } + } + /******write No Relax to 1******/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04);/*force update*/ + checkCMD(30); + TPD_DEBUG("forbid Forbid NoiseMitigation oK\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02);/*force Cal*/ + checkCMD(30); + TPD_DEBUG("Force Cal oK\n"); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x00);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*et report*/ + checkCMD(30); + + count = 0; + for (x = 0; x < TX_NUM; x++) { + + for (y = 0; y < RX_NUM; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + baseline_data = (tmp_h<<8) | tmp_l; + + if ((y < RX_NUM) && (x < TX_NUM)) { + if (x == (TX_NUM-1) && y == (RX_NUM-1)) + left_ramdata = baseline_data; + else if (x == (TX_NUM-1) && y == (RX_NUM-2)) + right_ramdata = baseline_data; + if (((baseline_data + 60) < + *(baseline_data_test + count*2)) + || ((baseline_data - 60) > + *(baseline_data_test + count*2 + 1))) { + if ((x == (TX_NUM-1) && + (y != RX_NUM-1 || y != RX_NUM-2)) + || (x != (TX_NUM-1) && + (y == RX_NUM-1 || y == RX_NUM-2))) { + count++; + continue; + } + TPD_ERR("TP failed,RX:%d,TX:%d\n" + "baseline_data is %d\n" + "TPK_limit[%d*2]=%d\n" + "TPK_limit[%d*2+1]=%d\n", + y, x, baseline_data, count, + *(baseline_data_test+count*2), count, + *(baseline_data_test+count*2 + 1)); + if ((baseline_data <= 0) + && (first_check == 0)) { + first_check = 1; + readdata_fail = 1; + } + num_read_chars += + snprintf(&(buf[num_read_chars]), 40, + "err baseline_data[%d][%d]=%d[%d,%d]\n" + , x, y, baseline_data, + *(baseline_data_test+count*2), + *(baseline_data_test+count*2 + 1)); + error_count++; + goto END; + } + } + count++; + } + } + + if (!enable_cbc) { + enable_cbc = 1; + + TPD_ERR("enable cbc baseline test again\n"); + goto TEST_WITH_CBC_s3508; + } + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x1); + if (ret < 0) { + TPD_ERR("%s line%d failed\n", __func__, __LINE__); + error_count++; + goto END; + } + + /*Step2 : Check trx-to-ground*/ + TPD_ERR("step 2:Check trx-to-ground\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x19);/*select report type 25*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE+1, 0x0); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(10); + tx_datal = i2c_smbus_read_i2c_block_data(ts->client, + F54_ANALOG_DATA_BASE+3, 7, buffer); + if (ts->support_1080x2160_tp) { + buffer[0] |= 0x20;/*no care 5 31 32 34 36 37 40 52 53chanel*/ + buffer[3] |= 0x80; + buffer[4] |= 0x35; + buffer[5] |= 0x01; + buffer[6] |= 0xc0; + } else { + buffer[0] |= 0x10;/*no care 4 31 32 40 50 51 52chanel*/ + buffer[3] |= 0x80; + buffer[5] |= 0x01; + buffer[6] |= 0xc0; + } + for (x = 0; x < 7; x++) { + if (buffer[x] != 0xff) { + error_count++; + TPD_ERR("step 2:error_count[%d] buff%d[0x%x] ERROR!\n", + error_count, x, buffer[x]); + goto END; + } + } + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("%s line%d failed\n", __func__, __LINE__); + error_count++; + goto END; + } + + ret = i2c_smbus_write_byte_data(ts->client, + F01_RMI_CMD_BASE, 0x01);/*software reset TP*/ + msleep(50); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x1); + if (ret < 0) { + TPD_ERR("%s line%d failed\n", __func__, __LINE__); + error_count++; + goto END; + } + + /*step 3 :check tx-to-tx and tx-to-vdd*/ + TPD_ERR("step 3:check TRx-TRx & TRx-Vdd short\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x1A);/*select report type 26*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x0); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(10); + tx_datal = i2c_smbus_read_i2c_block_data(ts->client, + F54_ANALOG_DATA_BASE+3, 7, buffer); + buffer[0] &= 0xef;/*no care 4 31 32 40 50 51 52chanel*/ + buffer[3] &= 0x7f; + buffer[5] &= 0xfe; + buffer[6] &= 0x3f; + for (x = 0; x < 7; x++) { + if (buffer[x]) { + error_count++; + TPD_ERR("step 3:error_count[%d] buff%d[0x%x] ERROR!\n", + error_count, x, buffer[x]); + goto END; + } + } + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("%s line%d failed\n", __func__, __LINE__); + error_count++; + goto END; + } + + ret = i2c_smbus_write_byte_data(ts->client, + F01_RMI_CMD_BASE, 0x01);/*software reset TP*/ + msleep(50); + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x1); + if (ret < 0) { + TPD_ERR("%s line%d failed\n", __func__, __LINE__); + error_count++; + goto END; + } + /*Step4 : Check RT133*/ + TPD_ERR("step 4:Check RT133\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x85);/*select report type 133*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x0);/*set fifo 0*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD_RT133(); + for (y = 0; y < RX_NUM; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + baseline_data = (tmp_h<<8) | tmp_l; + if (baseline_data > 100) { + error_count++; + TPD_ERR("4:error [%d] baseline %d[0x%x] ERROR!\n", + error_count, y, baseline_data); + goto END; + } + } + /*Step 5 : Check RT251 for random touch event*/ + TPD_ERR("Step 5 : Check RT251 for random touch event\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0xFB);/*select report type 0xFB*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE+1, 0x00);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(100); + + for (x = 0; x < TX_NUM; x++) { + + for (y = 0; y < RX_NUM; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE+3); + tmp_l = ret; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE+3); + tmp_h = ret; + baseline_data = (tmp_h << 8) | tmp_l; + if ((x < TX_NUM-1) && (y < RX_NUM-2) + && (baseline_data > 20)) { + if (++err_RT251 > + ((TX_NUM - 1) * (RX_NUM - 2) / 2)) { + error_count++; + TPD_ERR("err_RT251 = %d\n", err_RT251); + goto END; + } + } + + if ((x != TX_NUM - 1) && (y == RX_NUM - 1) + && baseline_data > 500) { + if (++err_RT251_self > (TX_NUM - 1) / 2) { + error_count++; + TPD_ERR("err_RT251_self = %d\n", + err_RT251_self); + goto END; + } + } + } + } + TPD_ERR("ROLAND----> err_RT251 is %d err_RT251_self is %d\n", + err_RT251, err_RT251_self); + /*Step 6 : Check RT252 for random touch event*/ + TPD_ERR("Step 6 : Check RT252 for random touch event\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0xFC);/*select report type 0xFC*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x00);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(70); + for (y = 0; y < RX_NUM + TX_NUM - 3; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + unsigned_baseline_data = (tmp_h << 8) | tmp_l; + if (unsigned_baseline_data < 10000) { + error_count++; + TPD_ERR("error_line is y =%d,data = %hu\n", + y, unsigned_baseline_data); + goto END; + } + } + /*Step 7 : Check RT253 for random touch event*/ + TPD_ERR("Step 7 : Check RT253 for random touch event\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0xFD);/*select report type 0xFD*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x00);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(70); + + for (x = 0; x < TX_NUM; x++) { + for (y = 0; y < RX_NUM; y++) { + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE+3); + tmp_l = ret; + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_DATA_BASE+3); + tmp_h = ret; + baseline_data = (tmp_h << 8) | tmp_l; + if (baseline_data > 20) { + if (++err_RT253 > (TX_NUM * RX_NUM) / 2) { + error_count++; + TPD_ERR("err_RT253 = %d\n", err_RT253); + goto END; + } + } + } + } + TPD_ERR("ROLAND----> err_RT253 is %d\n", err_RT253); +END: + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02); + delay_qt_ms(60); + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x00); + ret = i2c_smbus_write_byte_data(ts->client, F01_RMI_CMD00, 0x01); + msleep(150); + +#ifdef SUPPORT_GLOVES_MODE + synaptics_glove_mode_enable(ts); +#endif + synaptics_init_panel(ts); + synaptics_enable_interrupt(ts, 1); + touch_enable(ts); + TPD_ERR("\n\nstep5 reset and open irq complete\n"); + mutex_unlock(&ts->mutex); +#endif + if (readdata_fail == 1) { + TPD_ERR("readdata_fail...try again:%d\n", first_check); + readdata_fail = 0; + goto READDATA_AGAIN; + } +#ifdef ENABLE_TPEDGE_LIMIT + synaptics_tpedge_limitfunc(); +#endif + TPD_ERR("status...first_check:%d:readdata_fail:%d\n", + first_check, readdata_fail); + num_read_chars += snprintf(&(buf[num_read_chars]), + 40, "imageid=0x%x,deviceid=0x%x\n", TP_FW, TP_FW); + num_read_chars += snprintf(&(buf[num_read_chars]), + 24, "left:=%d,right:%d\n", left_ramdata, right_ramdata); + num_read_chars += snprintf(&(buf[num_read_chars]), 32, + "%d error(s). %s\n", error_count, error_count?"":"All test passed."); + return num_read_chars; +} + +static ssize_t tp_baseline_show_with_cbc(struct device_driver *ddri, char *buf) +{ + int ret = 0; + int x, y; + ssize_t num_read_chars = 0; + uint8_t tmp_l = 0, tmp_h = 0; + uint8_t tmp_old, tmp_new; + uint16_t count = 0; + struct synaptics_ts_data *ts = ts_g; + + if (ts->is_suspended == 1) + return count; + memset(delta_baseline, 0, sizeof(delta_baseline)); + if (!ts) + return 0; + /*disable irq when read data from IC*/ + touch_disable(ts); + mutex_lock(&ts->mutex); + synaptics_read_register_map_page1(ts); + TPD_DEBUG("\nstep 1:select report type 0x03 baseline\n"); + /*step 1:check raw capacitance*/ + + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_DATA_BASE, 0x03);/*select report type 0x03*/ + if (ret < 0) { + TPDTM_DMESG("step 1: select report type 0x03 failed\n"); + /*return sprintf(buf, "i2c err!");*/ + } + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 20, 0x01); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 23); + tmp_old = ret & 0xff; + TPD_DEBUG("ret = %x ,tmp_old =%x ,tmp_new = %x\n", + ret, tmp_old, (tmp_old | 0x10)); + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 23, (tmp_old | 0x10)); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + checkCMD(10); + TPD_DEBUG("open CBC oK\n"); + ret = i2c_smbus_read_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27); + tmp_new = ret | 0x20; + i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_CONTROL_BASE + 27, tmp_new); + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x04); + + + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0X04);/*force F54_ANALOG_CMD00*/ + checkCMD(10); + TPD_DEBUG("forbid Forbid NoiseMitigation oK\n"); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0X02);/*Force Cal, F54_ANALOG_CMD00*/ + checkCMD(10); + TPDTM_DMESG("Force Cal oK\n"); + + ret = synaptics_rmi4_i2c_write_word(ts->client, + F54_ANALOG_DATA_BASE + 1, 0x00);/*set fifo 00*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(10); + count = 0; + for (x = 0; x < TX_NUM; x++) { + TPD_DEBUG("\n[%d]", x); + num_read_chars += snprintf(&(buf[num_read_chars]), + 5, "[%d]", x); + for (y = 0; y < RX_NUM; y++) { + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_l = ret & 0xff; + ret = synaptics_rmi4_i2c_read_byte(ts->client, + F54_ANALOG_DATA_BASE + 3); + tmp_h = ret & 0xff; + delta_baseline[x][y] = (tmp_h<<8) | tmp_l; + TPD_DEBUG("%d,", delta_baseline[x][y]); + num_read_chars += snprintf(&(buf[num_read_chars]), + 8, "%5d", delta_baseline[x][y]); + } + num_read_chars += snprintf(&(buf[num_read_chars]), 2, "\n"); + } + num_read_chars += snprintf(&(buf[num_read_chars]), 2, "\n"); + ret = synaptics_rmi4_i2c_write_byte(ts->client, + F54_ANALOG_COMMAND_BASE, 0x02); + delay_qt_ms(60); + synaptics_enable_interrupt(ts, 1); + mutex_unlock(&ts->mutex); + touch_enable(ts); + return num_read_chars; +} + +static ssize_t synaptics_rmi4_baseline_show(struct device *dev, +char *buf, bool savefile) +{ + return synaptics_rmi4_baseline_show_s3508(dev, buf, savefile); +} + +static ssize_t tp_test_store(struct device_driver *ddri, + const char *buf, size_t count) +{ + TPDTM_DMESG("tp_test_store is not support\n"); + return count; +} + +static ssize_t synaptics_rmi4_vendor_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if ((tp_dev == TP_G2Y) || (tp_dev == TP_TPK)) + return snprintf(buf, 4, "%d\n", TP_TPK); + if (tp_dev == TP_TRULY) + return snprintf(buf, 4, "%d\n", TP_TRULY); + if (tp_dev == TP_OFILM) + return snprintf(buf, 4, "%d\n", TP_OFILM); + return snprintf(buf, 4, "%d\n", tp_dev); +} + + +static int synaptics_input_init(struct synaptics_ts_data *ts) +{ + int attr_count = 0; + int ret = 0; + + TPD_DEBUG("%s is called\n", __func__); + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + TPD_ERR("snps_ts_probe: Failed to allocate input device\n"); + return ret; + } + ts->input_dev->name = TPD_DEVICE; + ts->input_dev->dev.parent = &ts->client->dev; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(ABS_MT_TOUCH_MAJOR, ts->input_dev->absbit); + set_bit(ABS_MT_WIDTH_MAJOR, ts->input_dev->absbit); + set_bit(ABS_MT_POSITION_X, ts->input_dev->absbit); + set_bit(ABS_MT_POSITION_Y, ts->input_dev->absbit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); + set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit); +#ifdef SUPPORT_GESTURE + set_bit(KEY_F4, ts->input_dev->keybit);/*doulbe-tap resume*/ + set_bit(KEY_DOUBLE_TAP, ts->input_dev->keybit); + set_bit(KEY_GESTURE_CIRCLE, ts->input_dev->keybit); + set_bit(KEY_GESTURE_V, ts->input_dev->keybit); + set_bit(KEY_GESTURE_TWO_SWIPE, ts->input_dev->keybit); + set_bit(KEY_GESTURE_LEFT_V, ts->input_dev->keybit); + set_bit(KEY_GESTURE_RIGHT_V, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(KEY_BACK, ts->input_dev->keybit); +#endif + /* For multi touch */ + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, + 0, (ts->max_x-1), 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, + 0, (ts->max_y-1), 0, 0); +#ifdef REPORT_2D_PRESSURE + if (ts->support_ft) + input_set_abs_params(ts->input_dev, + ABS_MT_PRESSURE, 0, 255, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(ts->input_dev, ts->max_num, 0); +#endif + input_set_drvdata(ts->input_dev, ts); + + if (input_register_device(ts->input_dev)) { + TPD_ERR("%s: Failed to register input device\n", __func__); + input_unregister_device(ts->input_dev); + input_free_device(ts->input_dev); + return -ENOMEM; + } + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs_oem); attr_count++) { + ret = sysfs_create_file(&ts->input_dev->dev.kobj, + &attrs_oem[attr_count].attr); + if (ret < 0) { + dev_err(&ts->client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&ts->input_dev->dev.kobj, + &attrs_oem[attr_count].attr); + } + return -EINVAL; + } + } + return 0; +} + +#include "fw_update_v7.if" +static int check_hardware_version(struct device *dev) +{ + int ret; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + + if (!ts->client) { + TPD_ERR("i2c client point is NULL\n"); + return 0; + } + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request FW fail%s (%d)\n", ts->fw_name, ret); + return ret; + } + + ret = fwu_start_reflash_check(fw->data, ts->client); + release_firmware(fw); + if (ret < 0) + return -EINVAL; + else + return ret; +} +static int check_version; +/*********************FW Update Func******************************************/ +static int synatpitcs_fw_update(struct device *dev, bool force) +{ + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + int ret; + char fw_id_temp[12]; + uint8_t buf[4]; + uint32_t CURRENT_FIRMWARE_ID = 0; + + static bool check_onetime = true; + + TPD_DEBUG("%s is called\n", __func__); + if (!ts->client) { + TPD_ERR("i2c client point is NULL\n"); + return 0; + } + if (!strncmp(ts->manu_name, "S3718", 5)) { + if (check_onetime) { + check_onetime = false; + check_version = check_hardware_version(dev); + TPD_ERR("%s:first check hardware version %d\n", + __func__, check_version); + if (check_version < 0) { + TPD_ERR("checkversion fail....\n"); + return -EINVAL; + } + } + + if (check_version == 1) { + TPD_DEBUG("enter version 15801 update mode\n"); + strlcpy(ts->fw_name, "tp/fw_synaptics_15801.img", + sizeof(ts->fw_name)); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request FW fail %s (%d)\n", + ts->fw_name, ret); + return ret; + } + + } else { + TPD_DEBUG("enter version 15801 vb update mode\n"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request FW fail %s (%d)\n", + ts->fw_name, ret); + return ret; + } + } + + } else if (!strncmp(ts->manu_name, "s3508", 5) + || !strncmp(ts->manu_name, "15811", 5)) { + TPD_ERR("enter version 16859 update mode\n"); + ret = request_firmware(&fw, ts->fw_name, dev); + if (ret < 0) { + TPD_ERR("Request FW fail %s (%d)\n", ts->fw_name, ret); + return ret; + } + } else { + TPD_ERR("firmware name not match\n"); + return -EINVAL; + } + + ret = synapitcs_ts_update(ts->client, fw->data, fw->size, force); + if (ret < 0) { + TPD_ERR("FW update not success try again\n"); + ret = synapitcs_ts_update(ts->client, fw->data, fw->size, true); + if (ret < 0) { + TPD_ERR("FW update fail twice, quit update process!\n"); + return ret; + } + } + release_firmware(fw); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + ret = synaptics_rmi4_i2c_read_block(ts->client, + F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + snprintf(fw_id_temp, 12, "0x%x", CURRENT_FIRMWARE_ID); + strlcpy(ts->fw_id, fw_id_temp, sizeof(ts->fw_id)); + TP_FW = CURRENT_FIRMWARE_ID; + report_key_point_y = ts->max_y*button_map[2]/LCD_HEIGHT; +#ifdef SUPPORT_GLOVES_MODE + synaptics_glove_mode_enable(ts); +#endif + synaptics_init_panel(ts); + synaptics_enable_interrupt(ts, 1); + return 0; +} + +static ssize_t synaptics_update_fw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_ts_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 4, "%d\n", data->loading_fw); +} + +static ssize_t synaptics_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + unsigned long val; + int rc; + + if (ts->is_suspended && ts->support_hw_poweroff) { + TPD_ERR("power off firmware abort!\n"); + return size; + } + if (version_is_s3508) { + if (strncmp(ts->manu_name, "s3508", 5) + && strncmp(ts->manu_name, "15811", 5)) { + TPD_ERR("pdt name[%s] do not update!\n", ts->manu_name); + return size; + } + } else { + if (strncmp(ts->manu_name, "S3718", 5)) { + TPD_ERR("pdt name[%s] do not update!\n", ts->manu_name); + return size; + } + } + TPD_ERR("start update ******* fw_name:%s,ts->manu_name:%s\n", + ts->fw_name, ts->manu_name); + + if (size > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if (!val) + val = force_update; + + touch_disable(ts); + mutex_lock(&ts->mutex); + ts->loading_fw = true; + synatpitcs_fw_update(dev, val); + ts->loading_fw = false; + mutex_unlock(&ts->mutex); + touch_enable(ts); + force_update = 0; + return size; +} +/*********************FW Update Func End*************************************/ + + +static ssize_t synaptics_test_limit_show(struct device *dev, +struct device_attribute *attr, char *buf) +{ + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + int ret = 0; + uint16_t *prow = NULL; + uint16_t *prowcbc = NULL; + const struct firmware *fw = NULL; + struct test_header *ph = NULL; + int i = 0; + int temp = 0; + static int cat_cbc_change; + + ret = request_firmware(&fw, ts->test_limit_name, dev); + if (ret < 0) { + TPD_ERR("Request firmware failed - %s (%d)\n", + ts->test_limit_name, ret); + temp = temp + snprintf(&buf[temp], 35, + "Request failed,Check the path %d\n", temp); + return temp; + } + + ph = (struct test_header *)(fw->data); + prow = (uint16_t *)(fw->data + ph->array_limit_offset); + + prowcbc = (uint16_t *)(fw->data + ph->array_limitcbc_offset); + + TPD_DEBUG("snps_limit_show:limit_offset = %x limitcbc_offset = %x\n", + ph->array_limit_offset, ph->array_limitcbc_offset); + + TPD_DEBUG("test begin:\n"); + if (cat_cbc_change == 0 || ph->withCBC == 0) { + temp += snprintf(buf, 12, "Without cbc:"); + for (i = 0 ; i < (ph->array_limit_size/2); i++) { + if (i % (2*RX_NUM) == 0) + temp += snprintf(&(buf[temp]), 8, + "\n[%d] ", (i/RX_NUM)/2); + temp += snprintf(&buf[temp], + 4, "%d,", prow[i]); + TPD_ERR("%d,", prow[i]); + } + cat_cbc_change = 1; + } else { + temp += snprintf(buf, 10, "With cbc:"); + cat_cbc_change = 0; + + if (ph->withCBC == 0) + return temp; + + for (i = 0 ; i < (ph->array_limitcbc_size/2); i++) { + if (i % (2*RX_NUM) == 0) + temp += snprintf(&(buf[temp]), 8, + "\n[%d] ", (i/RX_NUM)/2); + temp += snprintf(&buf[temp], + 4, "%d,", prowcbc[i]); + TPD_ERR("%d,", prowcbc[i]); + } + } + release_firmware(fw); + return temp; +} + +static ssize_t synaptics_test_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return size; +} + +static ssize_t tp_doze_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + int doze_time = 0; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) + return snprintf(buf, 18, "switch page err\n"); + + doze_time = i2c_smbus_read_byte_data(ts->client, F01_RMI_CTRL02); + return snprintf(buf, 4, "%d\n", doze_time); +} + +static ssize_t tp_doze_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return size; +} + +static DEVICE_ATTR(test_limit, 0664, synaptics_test_limit_show, +synaptics_test_limit_store); +static DRIVER_ATTR(tp_baseline_image, 0664, tp_baseline_show, tp_delta_store); +static DRIVER_ATTR(tp_baseline_image_with_cbc, 0664, +tp_baseline_show_with_cbc, tp_test_store); +static DRIVER_ATTR(tp_delta_image, 0664, tp_rawdata_show, NULL); +static DRIVER_ATTR(tp_debug_log, 0664, tp_show, store_tp); +static DEVICE_ATTR(tp_fw_update, 0664, synaptics_update_fw_show, +synaptics_update_fw_store); +static DEVICE_ATTR(tp_doze_time, 0664, tp_doze_time_show, tp_doze_time_store); +static int synaptics_dsx_pinctrl_init(struct synaptics_ts_data *ts); + +static ssize_t tp_reset_write_func(struct file *file, +const char *buffer, size_t count, loff_t *ppos) +{ + int ret, write_flag, i; + struct synaptics_ts_data *ts = ts_g; + + if (ts->loading_fw) { + TPD_ERR("%s FW is updating break!!\n", __func__); + return count; + } + + ret = kstrtoint(buffer, 10, &write_flag); + TPD_ERR("%s write [%d]\n", __func__, write_flag); + if (write_flag == 1) { + ret = synaptics_soft_reset(ts); + } else if (write_flag == 2) { + synaptics_hard_reset(ts); + } else if (write_flag == 3) { + disable_irq_nosync(ts->irq); + } else if (write_flag == 4) { + enable_irq(ts->irq); + } else if (write_flag == 8) { + touch_enable(ts); + } else if (write_flag == 9) { + touch_disable(ts); + } else if (write_flag == 5) { + synaptics_read_register_map(ts); + } else if (write_flag == 6) { + for (i = 0; i < ts->max_num; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, 1); + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, 0); + } + #ifndef TYPE_B_PROTOCOL + input_mt_sync(ts->input_dev); + #endif + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + input_sync(ts->input_dev); + } + return count; +} + +static const struct file_operations base_register_address = { + .write = synap_write_address, + .read = synap_read_address, + .open = simple_open, + .owner = THIS_MODULE, +}; + + +static const struct file_operations i2c_device_test_fops = { + .read = i2c_device_test_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static const struct file_operations tp_baseline_test_proc_fops = { + .read = tp_baseline_test_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +#ifdef SUPPORT_GLOVES_MODE +static const struct file_operations glove_mode_enable_proc_fops = { + .write = tp_glove_write_func, + .read = tp_glove_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +#endif + +static const struct file_operations sleep_mode_enable_proc_fops = { + .write = tp_sleep_write_func, + .read = tp_sleep_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static const struct file_operations tp_reset_proc_fops = { + .write = tp_reset_write_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +static const struct file_operations vendor_id_proc_fops = { + .read = vendor_id_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +static int set_changer_bit(struct synaptics_ts_data *ts) +{ + int mode; + int ret; + + mode = i2c_smbus_read_byte_data(ts_g->client, F01_RMI_CTRL00); + + if (ts->changer_connet) + mode = mode | 0x20; + else + mode = mode & 0xDF; + + ret = i2c_smbus_write_byte_data(ts_g->client, F01_RMI_CTRL00, mode); + + return ret; +} +static ssize_t changer_read_func(struct file *file, char __user *user_buf, +size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + + ret = snprintf(page, 30, "the changer is %s!\n", + ts->changer_connet ? ("conneted"):("disconneted")); + + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + return ret; +} + +static ssize_t changer_write_func(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + struct synaptics_ts_data *ts = ts_g; + int ret = 0; + int rc; + + rc = kstrtoint(&buffer[0], 10, &ret); + if (rc < 0) + return rc; + if (!ts) + return count; + if ((ret == 0) || (ret == 1)) { + ts->changer_connet = ret; + ret = set_changer_bit(ts); + } + TPDTM_DMESG("%s:ts->changer_connet = %d\n", + __func__, ts->changer_connet); + + return count; +} +static const struct file_operations changer_ops = { + .write = changer_write_func, + .read = changer_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static void set_doze_time(int doze_time) +{ + static int pre_doze_time; + int ret = 0; + struct synaptics_ts_data *ts = ts_g; + + /* change to page 0 */ + if (ts == NULL) { + TPD_ERR("ts crash!\n"); + return; + } + if (pre_doze_time == doze_time) { + TPD_ERR("set time have already been set\n"); + return; + } + + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x00); + if (ret < 0) { + TPD_ERR("%s: chage page failed:%d\n", __func__, ret); + return; + } + + TPD_ERR("%s: set doze time: %d\n", __func__, doze_time); + ret = i2c_smbus_write_byte_data(ts->client, F01_RMI_CTRL02, doze_time); + if (ret < 0) { + TPD_ERR("%s: set doze time err:%d\n", __func__, ret); + return; + } + pre_doze_time = doze_time; + + /* use the read out circle to delay */ + ret = i2c_smbus_read_byte_data(ts->client, F01_RMI_CTRL02); + if (ret < 0) + return; + if (ret != doze_time) { + TPD_ERR("reset doze time\n"); + ret = i2c_smbus_write_byte_data(ts->client, + F01_RMI_CTRL02, doze_time); + if (ret < 0) { + TPD_ERR("%s: reset doze time err:%d\n", __func__, ret); + return; + } + } +} + +#define SUBABS(x, y) ((x)-(y)) +static int tp_baseline_get(struct synaptics_ts_data *ts, bool flag) +{ + int ret = 0; + int x, y; + uint8_t *value; + int k = 0; + + if (!ts) + return -ENOMEM; + + atomic_set(&ts->is_stop, 1); + touch_disable(ts); + TPD_DEBUG("%s start!\n", __func__); + value = kzalloc(TX_NUM*RX_NUM*2, GFP_KERNEL); + memset(delta_baseline, 0, sizeof(delta_baseline)); + + mutex_lock(&ts->mutex); + if (ts->gesture_enable) + synaptics_enable_interrupt_for_gesture(ts, false); + else + synaptics_mode_change(0x00);/*change getbase data*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x1); + + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_DATA_BASE, 0x03);/*select report type 0x03*/ + ret = i2c_smbus_write_word_data(ts->client, + F54_ANALOG_DATA_BASE + 1, 0);/*set fifo 00*/ + ret = i2c_smbus_write_byte_data(ts->client, + F54_ANALOG_COMMAND_BASE, 0x01);/*get report*/ + checkCMD(10); + + ret = synaptics_rmi4_i2c_read_block(ts->client, + F54_ANALOG_DATA_BASE + 3, 2*TX_NUM*RX_NUM, value); + for (x = 0; x < TX_NUM; x++) { + for (y = 0; y < RX_NUM; y++) { + delta_baseline[x][y] = (int16_t)(((uint16_t)(value[k])) + | ((uint16_t)(value[k+1] << 8))); + k = k + 2; + + if (flag) + delta[x][y] = SUBABS(delta_baseline[x][y], + baseline[x][y]); + else + baseline[x][y] = delta_baseline[x][y]; + } + } +/*ret = i2c_smbus_write_byte_data(ts->client, F54_ANALOG_COMMAND_BASE, 0X02);*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + ret = i2c_smbus_write_byte_data(ts->client, F01_RMI_CMD_BASE, 0x01); + + mutex_unlock(&ts->mutex); + atomic_set(&ts->is_stop, 0); + msleep(20); + if (ts->gesture_enable) + set_doze_time(1); + touch_enable(ts); +#ifdef ENABLE_TPEDGE_LIMIT + synaptics_tpedge_limitfunc(); +#endif + TPD_DEBUG("%s end!\n", __func__); + kfree(value); + return 0; +} +static void tp_baseline_get_work(struct work_struct *work) +{ + struct synaptics_ts_data *ts = ts_g; + + tp_baseline_get(ts, true);/*get the delta data*/ +} + +static ssize_t touch_press_status_read(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + int x, y; + int press_points = 0; + int points_misspresee = 0; + int str_n = 0; + + char *page = kzalloc(1024*2, GFP_KERNEL); + + if (!page) { + TPD_ERR("%s malloc memery error!", __func__); + return -ENOMEM; + } + TPD_ERR("%s", __func__); + + for (x = 0; x < TX_NUM; x++) { + for (y = 0; y < RX_NUM; y++) { + if (ts_g->support_1080x2160_tp) { + if (x > (TX_NUM-1) || y > (RX_NUM-15)) + continue; + } else { + if (x > (TX_NUM-1) || y < (RX_NUM-12)) + continue; + } + if ((delta[x][y] < -30) && (delta[x][y] > -250)) { +/*str_n += sprintf(&page[str_n],"x%d,y%d = %4d\n", x, y, delta[x][y]);*/ + press_points++; + } + if ((delta[x][y] > 30) && (delta[x][y] < 200)) + points_misspresee++; + } + + } + + if (points_misspresee > 4) + get_tp_base = 0; + TPD_ERR("points_mispressee num:%d,get_tp_base:%d\n", + points_misspresee, get_tp_base); + + str_n += snprintf(&page[str_n], 24, "\n%s %d PD > [25]\n", + (press_points > 4) ? "near":"away", press_points); + + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + + kfree(page); + return ret; +} + +static ssize_t touch_press_status_write(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + struct synaptics_ts_data *ts = ts_g; + int ret = 0; + int rc; + + rc = kstrtoint(&buffer[0], 10, &ret); + if (rc < 0) + return rc; + + if (!ts) + return count; + + TPD_ERR("%s write %d\n", __func__, ret); + if (ret == 0) { + tp_baseline_get(ts, false); + } else if (ret == 1) { + if (ts->gesture_enable == 0) + queue_delayed_work(get_base_report, + &ts->base_work, msecs_to_jiffies(120)); + else + queue_delayed_work(get_base_report, + &ts->base_work, msecs_to_jiffies(1)); + } + return count; +} +static const struct file_operations touch_press_status = { + .write = touch_press_status_write, + .read = touch_press_status_read, + .open = simple_open, + .owner = THIS_MODULE, +}; + +#ifdef ENABLE_TPEDGE_LIMIT +static ssize_t limit_enable_read(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + ssize_t ret = 0; + char page[PAGESIZE]; + + TPD_DEBUG("the limit_enable is: %d\n", limit_enable); + ret = snprintf(page, 4, "%d\n", limit_enable); + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + + return ret; +} + +static ssize_t limit_enable_write(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret; + char buf[8] = {0}; + int limit_mode = 0; + + if (count > 2) + count = 2; + if (ts_g == NULL) { + TPD_ERR("ts_g is NULL!\n"); + return -ENOMEM; + } + if (copy_from_user(buf, buffer, count)) { + TPD_DEBUG("%s: read proc input error.\n", __func__); + return count; + } + + if ('0' == buf[0]) + limit_enable = 0; + else if ('1' == buf[0]) + limit_enable = 1; + + msleep(30); + mutex_lock(&ts_g->mutex); + ret = i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + + limit_mode = i2c_smbus_read_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b); + + TPD_ERR("%s_proc limit_enable =%d,mode:0x%x !\n", + __func__, limit_enable, limit_mode); + + if (limit_mode) { + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + if (limit_enable == 0) { + limit_mode = limit_mode & 0xFE; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b, limit_mode); + } else if (limit_enable == 1) { + limit_mode = limit_mode | 0x1; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b, limit_mode); + } + } + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x0); + mutex_unlock(&ts_g->mutex); + return count; +} + +static const struct file_operations proc_limit_enable = { + .read = limit_enable_read, + .write = limit_enable_write, + .open = simple_open, + .owner = THIS_MODULE, +}; +#endif +#ifdef SUPPORT_TP_TOUCHKEY +static ssize_t key_switch_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + TPD_ERR("%s left:%s right:%s\n", __func__, + key_switch ? "key_back" : "key_appselect", + key_switch ? "key_appselect" : "key_back"); + ret = snprintf(page, 48, "key_switch left:%s right:%s\n", + key_switch ? "key_back" : "key_appselect", + key_switch ? "key_appselect" : "key_back"); + ret = simple_read_from_buffer(user_buf, + count, ppos, page, strlen(page)); + + return ret; +} + +static ssize_t key_switch_write_func(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[4] = {0}; + int ret; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return count; + if (count > 2) + return count; + if (copy_from_user(buf, buffer, count)) { + TPD_ERR("%s copy error\n", __func__); + return count; + } + ret = kstrtoint(&buf[0], 10, &key_switch); + if (ret < 0) + return ret; + TPD_ERR("%s write [%d]\n", __func__, key_switch); + TPD_ERR("left:%s right:%s\n", key_switch ? "key_back":"key_appselect", + key_switch ? "key_appselect":"key_back"); + return count; +} + +static const struct file_operations key_switch_proc_fops = { + .write = key_switch_write_func, + .read = key_switch_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +static ssize_t key_disable_read_func(struct file *file, +char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return ret; + + TPD_ERR("%s key_back:%s key_appselect:%s\n", __func__, + key_back_disable ? "disable":"enable", + key_appselect_disable ? "disable":"enable"); + + ret = snprintf(page, 64, + "cmd:enable,disable\nkey_back:%s key_appselect:%s\n", + key_back_disable ? "disable":"enable", + key_appselect_disable ? "disable":"enable"); + + ret = simple_read_from_buffer(user_buf, count, + ppos, page, strlen(page)); + + return ret; +} + +static ssize_t key_disable_write_func(struct file *file, +const char __user *buffer, size_t count, loff_t *ppos) +{ + char buf[PAGESIZE]; + struct synaptics_ts_data *ts = ts_g; + + if (!ts) + return count; + if (count > sizeof(buf)) { + TPD_ERR("%s error\n", __func__); + return count; + } + + if (copy_from_user(buf, buffer, count)) { + TPD_ERR("%s copy error\n", __func__); + return count; + } + if (strnstr(buf, "disable", sizeof(buf)) != NULL) { + key_back_disable = true; + key_appselect_disable = true; + } else if (strnstr(buf, "enable", sizeof(buf)) != NULL) { + key_back_disable = false; + key_appselect_disable = false; + } + TPD_ERR("%s key_back:%d key_appselect:%d\n", + __func__, key_back_disable, key_appselect_disable); + + return count; +} + +static const struct file_operations key_disable_proc_fops = { + .write = key_disable_write_func, + .read = key_disable_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; +#endif +static int init_synaptics_proc(void) +{ + int ret = 0; + struct proc_dir_entry *prEntry_tmp = NULL; + + prEntry_tp = proc_mkdir("touchpanel", NULL); + + if (prEntry_tp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create touchpanel\n"); + } + +#ifdef SUPPORT_GESTURE + prEntry_tmp = proc_create("gesture_enable", 0664, + prEntry_tp, &tp_gesture_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create gesture_enable\n"); + } + prEntry_tmp = proc_create("gesture_switch", 0664, + prEntry_tp, &gesture_switch_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create gesture_switch\n"); + } + prEntry_tmp = proc_create("coordinate", 0444, + prEntry_tp, &coordinate_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create coordinate\n"); + } +#endif + +#ifdef SUPPORT_GLOVES_MODE + prEntry_tmp = proc_create("glove_mode_enable", 0664, + prEntry_tp, &glove_mode_enable_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create glove_mode_enable\n"); + } +#endif + +#ifdef SUPPORT_TP_SLEEP_MODE + prEntry_tmp = proc_create("sleep_mode_enable", 0664, + prEntry_tp, &sleep_mode_enable_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create sleep_mode_enable\n"); + } +#endif + +#ifdef RESET_ONESECOND + prEntry_tmp = proc_create("tp_reset", 0664, + prEntry_tp, &tp_reset_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create tp_reset\n"); + } +#endif +#ifdef ENABLE_TPEDGE_LIMIT + prEntry_tmp = proc_create("tpedge_limit_enable", 0664, + prEntry_tp, &proc_limit_enable); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create tp_limit_enable\n"); + } +#endif + prEntry_tmp = proc_create("baseline_test", 0444, + prEntry_tp, &tp_baseline_test_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create baseline_test\n"); + } + prEntry_tmp = proc_create("i2c_device_test", 0664, + prEntry_tp, &i2c_device_test_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create i2c_device_test\n"); + } + + prEntry_tmp = proc_create("radd", 0774, + prEntry_tp, &base_register_address); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create radd\n"); + } + prEntry_tmp = proc_create("vendor_id", 0444, + prEntry_tp, &vendor_id_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create vendor_id\n"); + } + prEntry_tmp = proc_create("changer_connet", 0664, + prEntry_tp, &changer_ops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create changer_connet\n"); + } + + prEntry_tmp = proc_create("touch_press", 0664, + prEntry_tp, &touch_press_status); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create touch_press\n"); + } +#ifdef SUPPORT_TP_TOUCHKEY + prEntry_tmp = proc_create("key_switch", 0664, + prEntry_tp, &key_switch_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create key_switch\n"); + } + + prEntry_tmp = proc_create("key_disable", 0664, + prEntry_tp, &key_disable_proc_fops); + if (prEntry_tmp == NULL) { + ret = -ENOMEM; + TPD_ERR("Couldn't create key_disable\n"); + } +#endif + return ret; +} +/******************************end****************************/ + +/****************************S3203*****update**********************************/ +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 + +static void re_scan_PDT(struct i2c_client *client) +{ + uint8_t buf[8]; + + i2c_smbus_read_i2c_block_data(client, 0xE9, 6, buf); + SynaF34DataBase = buf[3]; + SynaF34QueryBase = buf[0]; + i2c_smbus_read_i2c_block_data(client, 0xE3, 6, buf); + SynaF01DataBase = buf[3]; + SynaF01CommandBase = buf[1]; + i2c_smbus_read_i2c_block_data(client, 0xDD, 6, buf); + + SynaF34Reflash_BlockNum = SynaF34DataBase; + SynaF34Reflash_BlockData = SynaF34DataBase + 1; + SynaF34ReflashQuery_BootID = SynaF34QueryBase; + SynaF34ReflashQuery_FlashPropertyQuery = SynaF34QueryBase + 1; + SynaF34ReflashQuery_FirmwareBlockSize = SynaF34QueryBase + 2; + SynaF34ReflashQuery_FirmwareBlockCount = SynaF34QueryBase + 3; + SynaF34ReflashQuery_ConfigBlockSize = SynaF34QueryBase + 3; + SynaF34ReflashQuery_ConfigBlockCount = SynaF34QueryBase + 3; + i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_FirmwareBlockSize, 2, buf); + SynaFirmwareBlockSize = buf[0] | (buf[1] << 8); + TPD_DEBUG("SynaFirmwareBlockSize 3310 is %d\n", SynaFirmwareBlockSize); + SynaF34_FlashControl = SynaF34DataBase + 2; +} +struct image_header { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_contain_bootloader:1; + unsigned char options_reserved:6; + unsigned char bootloader_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char reserved_20_2f[16]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + unsigned char ds_info[10]; + unsigned char reserved_4a_4f[6]; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct image_header_data { + bool contains_firmware_id; + unsigned int firmware_id; + unsigned int checksum; + unsigned int firmware_size; + unsigned int config_size; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +static unsigned int extract_uint_le(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static void parse_header(struct image_header_data *header, + const unsigned char *fw_image) +{ + struct image_header *data = (struct image_header *)fw_image; + + header->checksum = extract_uint_le(data->checksum); + TPD_DEBUG(" debug checksume is %x", header->checksum); + header->bootloader_version = data->bootloader_version; + TPD_DEBUG(" debug bootloader_version is %d\n", + header->bootloader_version); + + header->firmware_size = extract_uint_le(data->firmware_size); + TPD_DEBUG(" debug firmware_size is %x", header->firmware_size); + + header->config_size = extract_uint_le(data->config_size); + TPD_DEBUG(" debug header->config_size is %x", header->config_size); + + memcpy(header->product_id, data->product_id, sizeof(data->product_id)); + header->product_id[sizeof(data->product_id)] = 0; + + memcpy(header->product_info, data->product_info, + sizeof(data->product_info)); + + header->contains_firmware_id = data->options_firmware_id; + TPD_DEBUG(" debug header->contains_firmware_id is %x\n", + header->contains_firmware_id); + if (header->contains_firmware_id) + header->firmware_id = extract_uint_le(data->firmware_id); + +} + +static int checkFlashState(struct i2c_client *client) +{ + int ret; + int count = 0; + + ret = synaptics_rmi4_i2c_read_byte(client, SynaF34_FlashControl + 1); + while ((ret != 0x80) && (count < 8)) { + msleep(20); + ret = synaptics_rmi4_i2c_read_byte(client, + SynaF34_FlashControl + 1); + count++; + } + if (count == 8) + return 1; + else + return 0; +} + +static int synaptics_fw_check(struct synaptics_ts_data *ts) +{ + int ret; + uint8_t buf[4]; + uint32_t bootloader_mode; + int max_y_ic = 0; + int max_x_ic = 0; + + if (!ts) { + TPD_ERR("%s ts is NULL\n", __func__); + return -ENOMEM; + } + + ret = synaptics_enable_interrupt(ts, 0); + if (ret < 0) + TPDTM_DMESG("synaptics_ts_probe: disable interrupt failed\n"); + + /*read product id */ + ret = synaptics_read_product_id(ts); + if (ret) { + TPD_ERR("failed to read product info\n"); + return -EINVAL; + } + /*read max_x ,max_y*/ + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + ret = synaptics_rmi4_i2c_write_byte(ts->client, 0xff, 0x0); + if (ret < 0) { + TPD_ERR("snps write byte failed for page select\n"); + return -EINVAL; + } + } + + i2c_smbus_read_i2c_block_data(ts->client, F12_2D_CTRL08, 4, buf); + max_x_ic = ((buf[1]<<8)&0xffff) | (buf[0] & 0xffff); + max_y_ic = ((buf[3]<<8)&0xffff) | (buf[2] & 0xffff); + + TPD_ERR("max_x = %d,max_y = %d; max_x_ic = %d,max_y_ic = %d\n", + ts->max_x, ts->max_y, max_x_ic, max_y_ic); + if ((ts->max_x == 0) || (ts->max_y == 0)) { + ts->max_x = max_x_ic; + ts->max_y = max_y_ic; + } + bootloader_mode = synaptics_rmi4_i2c_read_byte(ts->client, + F01_RMI_DATA_BASE); + bootloader_mode = bootloader_mode & 0xff; + bootloader_mode = bootloader_mode & 0x40; + TPD_DEBUG("afte fw update, bootloader_mode = 0x%x\n", + bootloader_mode); + + if ((max_x_ic == 0) || (max_y_ic == 0) || (bootloader_mode == 0x40)) { + TPD_ERR("Something wrong\n Trying Update again\n"); + return -EINVAL; + } + return 0; +} + +static void re_scan_PDT_s3508(struct i2c_client *client) +{ + uint8_t buf[8]; + + i2c_smbus_read_i2c_block_data(client, 0xE9, 6, buf); + SynaF34DataBase = buf[3]; + SynaF34QueryBase = buf[0]; + i2c_smbus_read_i2c_block_data(client, 0xE3, 6, buf); + SynaF01DataBase = buf[3]; + SynaF01CommandBase = buf[1]; + i2c_smbus_read_i2c_block_data(client, 0xDD, 6, buf); + + SynaF34Reflash_BlockNum = SynaF34DataBase; + SynaF34Reflash_BlockData = SynaF34DataBase + 1; + SynaF34ReflashQuery_BootID = SynaF34QueryBase; + SynaF34ReflashQuery_FlashPropertyQuery = SynaF34QueryBase + 1; + SynaF34ReflashQuery_FirmwareBlockSize = SynaF34QueryBase + 2; + SynaF34ReflashQuery_FirmwareBlockCount = SynaF34QueryBase + 3; + SynaF34ReflashQuery_ConfigBlockSize = SynaF34QueryBase + 3; + SynaF34ReflashQuery_ConfigBlockCount = SynaF34QueryBase + 3; + i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_FirmwareBlockSize, 2, buf); + SynaFirmwareBlockSize = buf[0] | (buf[1] << 8); + TPD_DEBUG("SynaFirmwareBlockSize 3310 is %d\n", SynaFirmwareBlockSize); + SynaF34_FlashControl = SynaF34DataBase + 2; +} + +static int synapitcs_ts_update(struct i2c_client *client, +const uint8_t *data, uint32_t data_len, bool force) +{ + int ret, j; + uint8_t buf[8]; + uint8_t bootloder_id[10]; + uint16_t block, firmware, configuration; + uint32_t CURRENT_FIRMWARE_ID = 0, FIRMWARE_ID = 0; + const uint8_t *Config_Data = NULL; + const uint8_t *Firmware_Data = NULL; + struct image_header_data header; + struct synaptics_ts_data *ts = dev_get_drvdata(&client->dev); + + TPD_DEBUG("%s is called\n", __func__); + if (!client) + return -EINVAL; + if (!strncmp(ts->manu_name, "S3718", 5)) { + Config_Data = data + 0x8f0; + ret = synaptics_rmi4_i2c_write_byte(client, 0xff, 0x0); + ret = synaptics_rmi4_i2c_read_block(client, + F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + FIRMWARE_ID = (Config_Data[0] << 24) | (Config_Data[1] << 16) + | (Config_Data[2] << 8) | Config_Data[3]; + if (check_version == 1) + TPD_ERR("CUR_FW_ID:%x, FW_ID:%x,FW_NAME:%s\n", + CURRENT_FIRMWARE_ID, FIRMWARE_ID, ts->fw_name); + else + TPD_ERR("CUR_FW_ID:%xvB, FW_ID:%xvB,FW_NAME:%s\n", + CURRENT_FIRMWARE_ID, FIRMWARE_ID, ts->fw_name); + + if (!force) { + if (CURRENT_FIRMWARE_ID == FIRMWARE_ID) + return 0; + } + ret = fwu_start_reflash(data, client); + if (ret) + return -EINVAL; + } else if (!strncmp(ts->manu_name, "s3508", 5) + || !strncmp(ts->manu_name, "15811", 5)) { + parse_header(&header, data); + if ((header.firmware_size + header.config_size + 0x100) + > data_len) { + TPDTM_DMESG("data_len data_len = %d\n", data_len); + return -EINVAL; + } + Firmware_Data = data + 0x100; + Config_Data = Firmware_Data + header.firmware_size; + ret = i2c_smbus_write_byte_data(client, 0xff, 0x0); + + ret = i2c_smbus_read_i2c_block_data(client, + F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + FIRMWARE_ID = (Config_Data[0] << 24) | (Config_Data[1] << 16) + | (Config_Data[2] << 8) | Config_Data[3]; + TPD_ERR("15811CURRENT_FW_ID:%x----, FW_ID:%x----,FW_NAME:%s\n", + CURRENT_FIRMWARE_ID, FIRMWARE_ID, ts->fw_name); + TPD_ERR("synaptics force is %d\n", force); + if (!force) { + if (CURRENT_FIRMWARE_ID == FIRMWARE_ID) + return 0; + } + re_scan_PDT_s3508(client); + block = 16; + TPD_DEBUG("block is %d\n", block); + firmware = (header.firmware_size)/16; + TPD_DEBUG("firmware is %d\n", firmware); + configuration = (header.config_size)/16; + TPD_DEBUG("configuration is %d\n", configuration); + + ret = i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_BootID, 8, &(bootloder_id[0])); + TPD_DEBUG("bootloader id is %x\n", + (bootloder_id[1] << 8) | bootloder_id[0]); + ret = i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, &(bootloder_id[0x0])); + TPD_DEBUG("Write SynaF34_FlashControl is 0x00%x ret is %d\n", + SynaF34_FlashControl, ret); + + i2c_smbus_write_byte_data(client, SynaF34_FlashControl, 0x0F); + msleep(20); + TPD_DEBUG("attn step 4\n"); + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Get in prog:flashstate is %x\n", ret); + return -EINVAL; + } + ret = i2c_smbus_read_byte_data(client, 0x04); + TPD_DEBUG("The status(device state) is %x\n", ret); + ret = i2c_smbus_read_byte_data(client, F01_RMI_CTRL_BASE); + TPD_DEBUG("The status(control f01_RMI_CTRL_DATA) is %x\n", ret); + ret = i2c_smbus_write_byte_data(client, + F01_RMI_CTRL_BASE, ret & 0x04); + /********************get into prog end************/ + ret = i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, &(bootloder_id[0x0])); + TPD_DEBUG("ret is %d\n", ret); + re_scan_PDT_s3508(client); + i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_BootID, 2, buf); + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, buf); + i2c_smbus_write_byte_data(client, SynaF34_FlashControl, 0x03); + msleep(2500); + ret = i2c_smbus_read_byte_data(client, SynaF34_FlashControl); + if (ret != 0x00) + msleep(2000); + ret = i2c_smbus_read_byte_data(client, + SynaF34_FlashControl + 1); + TPDTM_DMESG("The status(erase) is %x\n", ret); + TPD_ERR("15811update----------update----------update!\n"); + TPD_DEBUG("cnt %d\n", firmware); + for (j = 0; j < firmware; j++) { + buf[0] = j & 0x00ff; + buf[1] = (j & 0xff00) >> 8; + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockNum, 2, buf); + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 16, &Firmware_Data[j * 16]); + + i2c_smbus_write_byte_data(client, + SynaF34_FlashControl, 0x02); + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Firmware:data is %x,time =%d\n", + ret, j); + return -EINVAL; + } + } + /*step 7 configure data*/ + for (j = 0; j < configuration; j++) { + /*a)write SynaF34Reflash_BlockNum to access*/ + buf[0] = j & 0x00ff; + buf[1] = (j & 0xff00) >> 8; + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockNum, 2, buf); + /*b) write data*/ + + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 16, &Config_Data[j * 16]); + + /*c) issue write*/ + i2c_smbus_write_byte_data(client, + SynaF34_FlashControl, 0x06); + /*d) wait attn*/ + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Configuration:data is %x,time =%d\n", + ret, j); + return -EINVAL; + } + } + /*step 1 issue reset*/ + i2c_smbus_write_byte_data(client, SynaF01CommandBase, 0X01); + } else { + parse_header(&header, data); + if ((header.firmware_size + header.config_size + 0x100) + > data_len) { + TPDTM_DMESG("data_len data_len = %d\n", data_len); + return -EINVAL; + } + + Firmware_Data = data + 0x100; + Config_Data = Firmware_Data + header.firmware_size; + ret = synaptics_rmi4_i2c_write_byte(client, 0xff, 0x0); + + ret = synaptics_rmi4_i2c_read_block(client, + F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + FIRMWARE_ID = (Config_Data[0] << 24) | (Config_Data[1] << 16) + | (Config_Data[2] << 8) | Config_Data[3]; + + if (!force) { + if (CURRENT_FIRMWARE_ID == FIRMWARE_ID) + return 0; + } + re_scan_PDT(client); + block = 16; + TPD_DEBUG("block is %d\n", block); + firmware = (header.firmware_size)/16; + TPD_DEBUG("firmware is %d\n", firmware); + configuration = (header.config_size)/16; + TPD_DEBUG("configuration is %d\n", configuration); + + + ret = i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_BootID, 8, &(bootloder_id[0])); + TPD_DEBUG("bootloader id is %x\n", + (bootloder_id[1] << 8) | bootloder_id[0]); + ret = i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, &(bootloder_id[0x0])); + TPDTM_DMESG("Write SynaF34_FlashControl is 0x00%x ret is %d\n", + SynaF34_FlashControl, ret); + + synaptics_rmi4_i2c_write_byte(client, + SynaF34_FlashControl, 0x0F); + msleep(20); + TPD_DEBUG("attn step 4\n"); + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Get in prog:flashstate is %x\n", ret); + return -EINVAL; + } + ret = i2c_smbus_read_byte_data(client, 0x04); + TPD_DEBUG("The status(device state) is %x\n", ret); + ret = i2c_smbus_read_byte_data(client, F01_RMI_CTRL_BASE); + TPD_DEBUG("The status(control f01_RMI_CTRL_DATA) is %x\n", ret); + ret = i2c_smbus_write_byte_data(client, + F01_RMI_CTRL_BASE, ret & 0x04); + /********************get into prog end************/ + ret = i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, &(bootloder_id[0x0])); + TPD_DEBUG("ret is %d\n", ret); + re_scan_PDT(client); + i2c_smbus_read_i2c_block_data(client, + SynaF34ReflashQuery_BootID, 2, buf); + i2c_smbus_write_i2c_block_data(client, + SynaF34Reflash_BlockData, 2, buf); + i2c_smbus_write_byte_data(client, SynaF34_FlashControl, 0x03); + msleep(2000); + ret = i2c_smbus_read_byte_data(client, SynaF34_FlashControl); + TPDTM_DMESG("going to area synaF34_FlashControl %d\n", ret); + + TPD_ERR("update----------firmware -----------update!\n"); + TPD_DEBUG("cnt %d\n", firmware); + for (j = 0; j < firmware; j++) { + buf[0] = j & 0x00ff; + buf[1] = (j & 0xff00) >> 8; + synaptics_rmi4_i2c_write_block(client, + SynaF34Reflash_BlockNum, 2, buf); + synaptics_rmi4_i2c_write_block(client, + SynaF34Reflash_BlockData, 16, &Firmware_Data[j * 16]); + synaptics_rmi4_i2c_write_byte(client, + SynaF34_FlashControl, 0x02); + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Firmware:flash data3 is %x,time =%d\n", + ret, j); + return -EINVAL; + } + } + /*step 7 configure data*/ + TPD_ERR("update----------configuration ----------update!\n"); + for (j = 0; j < configuration; j++) { + /*a)write SynaF34Reflash_BlockNum to access*/ + buf[0] = j&0x00ff; + buf[1] = (j&0xff00)>>8; + synaptics_rmi4_i2c_write_block(client, + SynaF34Reflash_BlockNum, 2, buf); + /*b) write data*/ + synaptics_rmi4_i2c_write_block(client, + SynaF34Reflash_BlockData, 16, &Config_Data[j*16]); + /*c) issue write*/ + synaptics_rmi4_i2c_write_byte(client, + SynaF34_FlashControl, 0x06); + /*d) wait attn*/ + ret = checkFlashState(client); + if (ret > 0) { + TPD_ERR("Configuration:data is %x,time =%d\n", + ret, j); + return -EINVAL; + } + } + + /*step 1 issue reset*/ + synaptics_rmi4_i2c_write_byte(client, SynaF01CommandBase, 0x01); + } + /*step2 wait ATTN*/ + msleep(1500); + synaptics_read_register_map(ts); + /*FW flash check!*/ + ret = synaptics_fw_check(ts); + if (ret < 0) { + TPD_ERR("Firmware self check failed\n"); + return -EINVAL; + } + TPD_ERR("Firmware self check Ok\n"); + return 0; +} +#ifdef ENABLE_TPEDGE_LIMIT +static void synaptics_tpedge_limitfunc(void) +{ + int limit_mode = 0; + int ret; + + TPD_ERR("%s line %d F51_GRIP_CONFIGURATION = 0x%x\n", + __func__, __LINE__, F51_CUSTOM_CTRL_BASE+0x1b); + msleep(60); + ret = i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + limit_mode = i2c_smbus_read_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b); + TPD_ERR("%s limit_enable =%d,mode:0x%x !\n", + __func__, limit_enable, limit_mode); + if (limit_mode) { + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x4); + if (limit_enable == 0) { + if (limit_mode & 0x1) { + limit_mode = limit_mode & 0xFE; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b, limit_mode); + } + } else if (limit_enable == 1) { + if (!(limit_mode & 0x1)) { + limit_mode = limit_mode | 0x1; + ret = i2c_smbus_write_byte_data(ts_g->client, + F51_CUSTOM_CTRL_BASE+0x1b, limit_mode); + } + } + } + i2c_smbus_write_byte_data(ts_g->client, 0xff, 0x0); +} + +#endif +static int synaptics_soft_reset(struct synaptics_ts_data *ts) +{ + int ret; + + if (ts->loading_fw) { + TPD_ERR("%s FW is updating break!\n", __func__); + return -EINVAL; + } + touch_disable(ts); + ret = i2c_smbus_write_byte_data(ts->client, F01_RMI_CMD_BASE, 0x01); + if (ret < 0) + TPD_ERR("reset error ret=%d\n", ret); + TPD_ERR("%s !!!\n", __func__); + msleep(100); + touch_enable(ts); +#ifdef ENABLE_TPEDGE_LIMIT + synaptics_tpedge_limitfunc(); +#endif + return ret; +} +static void synaptics_hard_reset(struct synaptics_ts_data *ts) +{ + if (ts->reset_gpio > 0) { + gpio_set_value(ts->reset_gpio, 0); + msleep(20); + gpio_set_value(ts->reset_gpio, 1); + msleep(100); + TPD_ERR("%s !!!\n", __func__); + } + +} +static int synaptics_parse_dts(struct device *dev, struct synaptics_ts_data *ts) +{ + int rc; + struct device_node *np; + int temp_array[2]; + u32 voltage_supply[2]; + u32 current_supply; + + np = dev->of_node; + ts->irq_gpio = of_get_named_gpio_flags(np, "synaptics,irq-gpio", + 0, &(ts->irq_flags)); + if (ts->irq_gpio < 0) + TPD_DEBUG("ts->irq_gpio not specified\n"); + + ts->reset_gpio = of_get_named_gpio(np, "synaptics,reset-gpio", 0); + if (ts->reset_gpio < 0) + TPD_DEBUG("ts->reset-gpio not specified\n"); + + ts->v1p8_gpio = of_get_named_gpio(np, "synaptics,1v8-gpio", 0); + if (ts->v1p8_gpio < 0) + TPD_DEBUG("ts->1v8-gpio not specified\n"); + + if (of_property_read_bool(np, "oem,support_1080x2160_tp")) + ts->support_1080x2160_tp = true; + else + ts->support_1080x2160_tp = false; + + if (of_property_read_bool(np, "oem,support_hw_poweroff")) + ts->support_hw_poweroff = true; + else + ts->support_hw_poweroff = false; + + TPD_ERR("%s ts->support_hw_poweroff =%d\n", + __func__, ts->support_hw_poweroff); + + ts->enable2v8_gpio = of_get_named_gpio(np, + "synaptics,enable2v8-gpio", 0); + if (ts->enable2v8_gpio < 0) + TPD_DEBUG("ts->enable2v8_gpio not specified\n"); + + rc = of_property_read_u32(np, "synaptics,max-num-support", + &ts->max_num); + if (rc) { + TPD_DEBUG("ts->max_num not specified\n"); + ts->max_num = 10; + } + + rc = of_property_read_u32_array(np, "synaptics,button-map", + button_map, 3); + if (rc) + TPD_DEBUG("button-map not specified\n"); + TPD_DEBUG("synaptics:button map readed is %d %d %d\n", + button_map[0], button_map[1], button_map[2]); + + rc = of_property_read_u32_array(np, "synaptics,tx-rx-num", + tx_rx_num, 2); + if (rc) { + TPD_ERR("button-map not specified\n"); + TX_NUM = 30; + RX_NUM = 17; + } else { + TX_NUM = tx_rx_num[0]; + RX_NUM = tx_rx_num[1]; + } + TPD_ERR("synaptics,tx-rx-num is %d %d\n", TX_NUM, RX_NUM); + + rc = of_property_read_u32_array(np, "synaptics,display-coords", + temp_array, 2); + if (rc) { + TPD_ERR("lcd size not specified\n"); + LCD_WIDTH = 1080; + LCD_HEIGHT = 1920; + } else { + LCD_WIDTH = temp_array[0]; + LCD_HEIGHT = temp_array[1]; + } + + rc = of_property_read_u32_array(np, "synaptics,panel-coords", + temp_array, 2); + if (rc) { + ts->max_x = 1080; + ts->max_y = 1920; + } else { + ts->max_x = temp_array[0]; + ts->max_y = temp_array[1]; + } + + TPDTM_DMESG("synaptic:ts->irq_gpio:%d irq_flags:%u max_num %d\n", + ts->irq_gpio, ts->irq_flags, ts->max_num); + + /***********power regulator_get****************/ + ts->vdd_2v8 = regulator_get(&ts->client->dev, "vdd_2v8"); + if (IS_ERR(ts->vdd_2v8)) { + rc = PTR_ERR(ts->vdd_2v8); + TPD_DEBUG("Regulator get failed vdd rc=%d\n", rc); + } + rc = of_property_read_u32(np, "synaptics,avdd-current", + ¤t_supply); + if (rc < 0) + TPD_ERR("%s: Failed to get regulator vdd current\n", __func__); + ts->regulator_vdd_current = current_supply; + + rc = regulator_set_load(ts->vdd_2v8, ts->regulator_vdd_current); + if (rc < 0) + TPD_ERR("%s: Failed to set regulator current vdd\n", __func__); + + rc = of_property_read_u32_array(np, "synaptics,avdd-voltage", + voltage_supply, 2); + if (rc < 0) + TPD_ERR("%s: Failed to get regulator vdd voltage\n", __func__); + ts->regulator_vdd_vmin = voltage_supply[0]; + ts->regulator_vdd_vmax = voltage_supply[1]; + + rc = regulator_set_voltage(ts->vdd_2v8, ts->regulator_vdd_vmin, + ts->regulator_vdd_vmax); + if (rc < 0) + TPD_ERR("%s:00Failed to set regulator voltage vdd\n", __func__); + + ts->vcc_i2c_1v8 = regulator_get(&ts->client->dev, "vcc_i2c_1v8"); + if (IS_ERR(ts->vcc_i2c_1v8)) { + rc = PTR_ERR(ts->vcc_i2c_1v8); + TPD_DEBUG("Regulator get failed vcc_i2c rc=%d\n", rc); + } + + rc = of_property_read_u32(np, "synaptics,vdd-current", ¤t_supply); + if (rc < 0) + TPD_ERR("%s: Failed to get regulator vdd current\n", __func__); + ts->regulator_vdd_current = current_supply; + + rc = regulator_set_load(ts->vcc_i2c_1v8, ts->regulator_vdd_current); + if (rc < 0) + TPD_ERR("%s: Failed to set regulator current vdd\n", __func__); + + rc = of_property_read_u32_array(np, "synaptics,vdd-voltage", + voltage_supply, 2); + if (rc < 0) + TPD_ERR("%s: Failed to get regulator vdd voltage\n", __func__); + + ts->regulator_vdd_vmin = voltage_supply[0]; + ts->regulator_vdd_vmax = voltage_supply[1]; + + rc = regulator_set_voltage(ts->vcc_i2c_1v8, ts->regulator_vdd_vmin, + ts->regulator_vdd_vmax); + if (rc < 0) + TPD_ERR("%s:00Failed to set regulator voltage vdd\n", __func__); + + if (ts->reset_gpio > 0) { + if (gpio_is_valid(ts->reset_gpio)) { + rc = gpio_request(ts->reset_gpio, "tp-s3320-reset"); + if (rc) + TPD_ERR("unable to request reset_gpio [%d]\n", + ts->reset_gpio); + gpio_direction_output(ts->reset_gpio, 0); + } + } + if (ts->v1p8_gpio > 0) { + if (gpio_is_valid(ts->v1p8_gpio)) { + rc = gpio_request(ts->v1p8_gpio, "tp-s3320-1v8"); + if (rc) { + TPD_ERR("unable to request v1p8_gpio [%d]\n", + ts->v1p8_gpio); + } + } + } + + if (ts->enable2v8_gpio > 0) { + if (gpio_is_valid(ts->enable2v8_gpio)) { + rc = gpio_request(ts->enable2v8_gpio, + "rmi4-enable2v8-gpio"); + if (rc) + TPD_ERR("unable to request en2v8_gpio [%d]\n", + ts->enable2v8_gpio); + + } + } + + return rc; +} + +static int synaptics_dsx_pinctrl_init(struct synaptics_ts_data *ts) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ts->pinctrl = devm_pinctrl_get((ts->dev)); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + retval = PTR_ERR(ts->pinctrl); + TPD_ERR("%s pinctrl error!\n", __func__); + goto err_pinctrl_get; + } + + ts->pinctrl_state_active + = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(ts->pinctrl_state_active)) { + retval = PTR_ERR(ts->pinctrl_state_active); + TPD_ERR("%s pinctrl state active error!\n", __func__); + goto err_pinctrl_lookup; + } + + ts->pinctrl_state_suspend + = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ts->pinctrl_state_suspend)) { + retval = PTR_ERR(ts->pinctrl_state_suspend); + TPD_ERR("%s pinctrl state suspend error!\n", __func__); + goto err_pinctrl_lookup; + } + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ts->pinctrl); +err_pinctrl_get: + ts->pinctrl = NULL; + return retval; +} + +#ifdef SUPPORT_VIRTUAL_KEY +#define VK_KEY_X 180 +#define VK_CENTER_Y 2020 +#define VK_WIDTH 170 +#define VK_HIGHT 200 +static ssize_t vk_syna_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int len; + + len = snprintf(buf, 54, + ":%d:%d:%d:%d :%d:%d:%d:%d :%d:%d:%d:%d\n", + VK_KEY_X, VK_CENTER_Y, VK_WIDTH, VK_HIGHT, + VK_KEY_X*3, VK_CENTER_Y, VK_WIDTH, VK_HIGHT, + VK_KEY_X*5, VK_CENTER_Y, VK_WIDTH, VK_HIGHT); + + return len; +} + +static struct kobj_attribute vk_syna_attr = { + .attr = { + .name = "virtualkeys."TPD_DEVICE, + .mode = S_IRUGO, + }, + .show = &vk_syna_show, +}; + +static struct attribute *syna_properties_attrs[] = { + &vk_syna_attr.attr, + NULL +}; + +static struct attribute_group syna_properties_attr_group = { + .attrs = syna_properties_attrs, +}; +static int synaptics_ts_init_virtual_key(struct synaptics_ts_data *ts) +{ + int ret = 0; + + /* virtual keys */ + if (ts->properties_kobj) + return 0; + ts->properties_kobj = kobject_create_and_add("board_properties", NULL); + if (ts->properties_kobj) + ret = sysfs_create_group(ts->properties_kobj, + &syna_properties_attr_group); + + if (!ts->properties_kobj || ret) + printk("%s: failed to create board_properties\n", __func__); + /* virtual keys */ + return ret; +} +#endif + +static int synaptics_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_SYNAPTIC_RED + struct remotepanel_data *premote_data = NULL; +#endif + struct synaptics_ts_data *ts = NULL; + int ret = -1; + uint8_t buf[4]; + uint32_t CURRENT_FIRMWARE_ID = 0; + uint32_t bootloader_mode; + + TPD_ERR("%s is called\n", __func__); + + ts = kzalloc(sizeof(struct synaptics_ts_data), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + ts->dev = &client->dev; + ts->loading_fw = false; + ts->support_ft = true; + ts_g = ts; + get_tp_base = 0; + + synaptics_parse_dts(&client->dev, ts); + + /***power_init*****/ + ret = tpd_power(ts, 1); + if (ret < 0) + TPD_ERR("regulator_enable is called\n"); + ret = synaptics_dsx_pinctrl_init(ts); + if (!ret && ts->pinctrl) { + ret = pinctrl_select_state(ts->pinctrl, + ts->pinctrl_state_active); + } + + msleep(100);/*after poweron need sometime from bootloader to ui mode*/ + mutex_init(&ts->mutex); + mutex_init(&ts->mutexreport); + atomic_set(&ts->irq_enable, 0); + + ts->is_suspended = 0; + atomic_set(&ts->is_stop, 0); + spin_lock_init(&ts->lock); + /*****power_end*********/ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + TPD_ERR("%s [ERR]need I2C_FUNC_I2C\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ret = synaptics_rmi4_i2c_read_byte(client, 0x13); + if (ret < 0) { + ret = synaptics_rmi4_i2c_read_byte(client, 0x13); + if (ret < 0) { + #ifdef SUPPORT_VIRTUAL_KEY + virtual_key_enable = 0;/*no valid report key*/ + #endif + TPD_ERR("tp is no exist!\n"); + goto err_check_functionality_failed; + } + } + + ts->i2c_device_test = ret; + + synaptics_read_register_map(ts); + bootloader_mode = synaptics_rmi4_i2c_read_byte(ts->client, + F01_RMI_DATA_BASE); + + bootloader_mode = bootloader_mode&0x40; + TPD_ERR("before fw update bootloader_mode[0x%x]\n", bootloader_mode); + + synaptics_rmi4_i2c_read_block(ts->client, F34_FLASH_CTRL00, 4, buf); + CURRENT_FIRMWARE_ID = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]; + TPD_ERR("CURRENT_FIRMWARE_ID = 0x%x\n", CURRENT_FIRMWARE_ID); + TP_FW = CURRENT_FIRMWARE_ID; + snprintf(ts->fw_id, 12, "0x%x", TP_FW); + + memset(ts->fw_name, 0, TP_FW_NAME_MAX_LEN); + memset(ts->test_limit_name, 0, TP_FW_NAME_MAX_LEN); + + synaptics_rmi4_i2c_read_block(ts->client, F01_RMI_QUERY11, + sizeof(ts->manu_name), ts->manu_name); + if (!strncmp(ts->manu_name, "S3718", 5)) { + strlcpy(ts->fw_name, "tp/fw_synaptics_15801b.img", + sizeof(ts->fw_name)); + version_is_s3508 = 0; + } else { + if (ts->support_1080x2160_tp) + strlcpy(ts->fw_name, "tp/fw_synaptics_17801.img", + sizeof(ts->fw_name)); + else + strlcpy(ts->fw_name, "tp/fw_synaptics_16859.img", + sizeof(ts->fw_name)); + + version_is_s3508 = 1; + } + + strlcpy(ts->test_limit_name, "tp/14049/14049_Limit_jdi.img", + sizeof(ts->test_limit_name)); + TPD_DEBUG("synatpitcs_fw: fw_name = %s,ts->manu_name:%s\n", + ts->fw_name, ts->manu_name); + + push_component_info(TOUCH_KEY, ts->fw_id, ts->manu_name); + push_component_info(TP, ts->fw_id, ts->manu_name); + + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) { + ret = -ENOMEM; + goto exit_createworkqueue_failed; + } + INIT_DELAYED_WORK(&ts->speed_up_work, speedup_synaptics_resume); + + + memset(baseline, 0, sizeof(baseline)); + get_base_report = create_singlethread_workqueue("get_base_report"); + if (!get_base_report) { + ret = -ENOMEM; + goto exit_createworkqueue_failed; + } + INIT_DELAYED_WORK(&ts->base_work, tp_baseline_get_work); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) + TPD_ERR("synaptics_init_panel failed\n"); + + ret = synaptics_fw_check(ts); + if (ret < 0) { + force_update = 1; + TPD_ERR("This FW need to be updated!\n"); + } else { + force_update = 0; + } + /*disable interrupt*/ + ret = synaptics_enable_interrupt(ts, 0); + if (ret < 0) + TPD_ERR("synaptics_ts_probe: disable interrupt failed\n"); + ret = synaptics_soft_reset(ts); + if (ret < 0) + TPD_ERR("%s faile to reset device\n", __func__); + ret = synaptics_input_init(ts); + if (ret < 0) + TPD_ERR("synaptics_input_init failed!\n"); +#if defined(CONFIG_FB) + ts->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + if (ret) + TPD_ERR("Unable to register fb_notifier: %d\n", ret); +#endif + + + +#ifndef TPD_USE_EINT + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(3, 0), HRTIMER_MODE_REL); +#endif + +#ifdef TPD_USE_EINT + /**************** should set the irq GPIO *******************/ + if (gpio_is_valid(ts->irq_gpio)) { + /* configure touchscreen irq gpio */ + ret = gpio_request(ts->irq_gpio, "tp-s3320-irq"); + if (ret) + TPD_ERR("unable to request gpio [%d]\n", ts->irq_gpio); + ret = gpio_direction_input(ts->irq_gpio); + msleep(50); + ts->irq = gpio_to_irq(ts->irq_gpio); + } + TPD_ERR("synaptic:ts->irq is %d\n", ts->irq); + + ret = request_threaded_irq(ts->irq, NULL, + synaptics_irq_thread_fn, + ts->irq_flags | IRQF_ONESHOT, + TPD_DEVICE, ts); + if (ret < 0) + TPD_ERR("%s request_threaded_irq ret is %d\n", __func__, ret); + msleep(20); + ret = synaptics_enable_interrupt(ts, 1); + if (ret < 0) + TPD_ERR("%s enable interrupt error ret=%d\n", __func__, ret); +#endif + + if (device_create_file(&client->dev, &dev_attr_test_limit)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } + TPD_DEBUG("synaptics_ts_probe: going to create files--tp_fw_update\n"); + if (device_create_file(&client->dev, &dev_attr_tp_fw_update)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } + if (device_create_file(&client->dev, &dev_attr_tp_doze_time)) { + TPDTM_DMESG("device_create_file failt\n"); + goto exit_init_failed; + } + if (driver_create_file(&tpd_i2c_driver.driver, + &driver_attr_tp_debug_log)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } + if (driver_create_file(&tpd_i2c_driver.driver, + &driver_attr_tp_baseline_image_with_cbc)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } + if (driver_create_file(&tpd_i2c_driver.driver, + &driver_attr_tp_baseline_image)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } + if (driver_create_file(&tpd_i2c_driver.driver, + &driver_attr_tp_delta_image)) { + TPDTM_DMESG("driver_create_file failt\n"); + goto exit_init_failed; + } +#ifdef SUPPORT_VIRTUAL_KEY + synaptics_ts_init_virtual_key(ts); +#endif +#ifdef CONFIG_SYNAPTIC_RED + premote_data = remote_alloc_panel_data(); + if (premote_data) { + premote_data->client = client; + premote_data->input_dev = ts->input_dev; + premote_data->pmutex = &ts->mutex; + premote_data->irq_gpio = ts->irq_gpio; + premote_data->irq = client->irq; + premote_data->enable_remote = &(ts->enable_remote); + register_remote_device(premote_data); + + } +#endif + init_synaptics_proc(); + TPDTM_DMESG("synaptics_ts_probe 3203: normal end\n"); + return 0; + +exit_init_failed: + free_irq(client->irq, ts); +exit_createworkqueue_failed: + destroy_workqueue(synaptics_wq); + synaptics_wq = NULL; + destroy_workqueue(synaptics_report); + synaptics_report = NULL; + destroy_workqueue(get_base_report); + get_base_report = NULL; + +err_check_functionality_failed: + tpd_power(ts, 0); +err_alloc_data_failed: + tpd_i2c_driver.driver.pm = NULL; + kfree(ts); + ts = NULL; + ts_g = NULL; + TPD_ERR("synaptics_ts_probe: not normal end\n"); + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + int attr_count; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + TPD_ERR("%s is called\n", __func__); +#ifdef CONFIG_SYNAPTIC_RED + unregister_remote_device(); +#endif + +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + +#ifndef TPD_USE_EINT + hrtimer_cancel(&ts->timer); +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs_oem); attr_count++) + sysfs_remove_file(&ts->input_dev->dev.kobj, + &attrs_oem[attr_count].attr); + + input_unregister_device(ts->input_dev); + input_free_device(ts->input_dev); + kfree(ts); + tpd_power(ts, 0); + return 0; +} + +static int synaptics_ts_suspend(struct device *dev) +{ + int ret, i; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + TPD_ERR("input_dev registration is not complete\n"); + return -ENOMEM; + } + TPD_DEBUG("%s enter\n", __func__); + + if (ts->pre_btn_state & 0x01) {/*if press key and suspend release key*/ + ts->pre_btn_state &= 0x02;/*clear bit0*/ + input_report_key(ts->input_dev, OEM_KEY_BACK, 0); + input_sync(ts->input_dev); + } else if (ts->pre_btn_state & 0x02) { + ts->pre_btn_state &= 0x01;/*clear bit1*/ + input_report_key(ts->input_dev, OEM_KEY_APPSELECT, 0); + input_sync(ts->input_dev); + } + for (i = 0; i < ts->max_num; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + input_sync(ts->input_dev); + +#ifndef TPD_USE_EINT + hrtimer_cancel(&ts->timer); +#endif + +#ifdef SUPPORT_GESTURE + if (ts->gesture_enable) { + atomic_set(&ts->is_stop, 0); + if (mutex_trylock(&ts->mutex)) { + touch_enable(ts); + synaptics_enable_interrupt_for_gesture(ts, 1); + mutex_unlock(&ts->mutex); + TPD_ERR("enter gesture mode\n"); + } + set_doze_time(2); + } else { + ret = synaptics_mode_change(0x01); + /*when gesture disable TP sleep eary*/ + if (ret < 0) + TPD_ERR("%s line%d ERROR %d!\n", + __func__, __LINE__, ret); + } +#endif + TPD_DEBUG("%s normal end\n", __func__); + return 0; +} + +static void speedup_synaptics_resume(struct work_struct *work) +{ + int ret; + struct synaptics_ts_data *ts = ts_g; + +/*#ifdef SUPPORT_SLEEP_POWEROFF*/ + TPD_DEBUG("%s enter!\n", __func__); + if (ts->support_hw_poweroff) { + if (ts->gesture_enable == 0) { + if (ts->pinctrl) + ret = pinctrl_select_state(ts->pinctrl, + ts->pinctrl_state_active); + ret = tpd_power(ts, 1); + if (ret < 0) + TPD_ERR("%s power on err\n", __func__); + } + } + TPD_DEBUG("%s end!\n", __func__); +/*#endif*/ +} + +static int synaptics_ts_resume(struct device *dev) +{ + int ret; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + int i; + + TPD_DEBUG("%s enter!\n", __func__); + + if (ts->loading_fw) { + TPD_ERR("%s FW is updating break!\n", __func__); + return -EINVAL; + } + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + TPD_ERR("input_dev registration is not complete\n"); + goto ERR_RESUME; + } + for (i = 0; i < ts->max_num; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + input_sync(ts->input_dev); + + /*touch_enable(ts);*/ + + TPD_DEBUG("%s:normal end!\n", __func__); +ERR_RESUME: + return 0; +} + +static int synaptics_i2c_suspend(struct device *dev) +{ + int ret; + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + TPD_DEBUG("%s: is called\n", __func__); + if (ts->gesture_enable == 1) { + /*enable gpio wake system through intterrupt*/ + enable_irq_wake(ts->irq); + } +/*#ifdef SUPPORT_SLEEP_POWEROFF*/ + if (ts->loading_fw) { + TPD_ERR("FW is updating while suspending"); + return -EINVAL; + } + if (ts->support_hw_poweroff && (ts->gesture_enable == 0)) { + ret = tpd_power(ts, 0); + if (ret < 0) + TPD_ERR("%s power off err\n", __func__); + if (ts->pinctrl) { + ret = pinctrl_select_state(ts->pinctrl, + ts->pinctrl_state_suspend); + } + } +/*#endif*/ + return 0; +} + +static int synaptics_i2c_resume(struct device *dev) +{ + struct synaptics_ts_data *ts = dev_get_drvdata(dev); + + TPD_DEBUG("%s is called\n", __func__); + queue_delayed_work(synaptics_wq, &ts->speed_up_work, + msecs_to_jiffies(1)); + if (ts->gesture_enable == 1) { + /*disable gpio wake system through intterrupt*/ + disable_irq_wake(ts->irq); + } + return 0; +} + +static int synaptics_mode_change(int mode) +{ + int ret; + int tmp_mode; + + tmp_mode = i2c_smbus_read_byte_data(ts_g->client, F01_RMI_CTRL00); + tmp_mode = tmp_mode & 0xF8;/*bit0-bit2(mode)*/ + tmp_mode = tmp_mode | mode; + if (ts_g->changer_connet) + tmp_mode = tmp_mode | 0x20;/*set bit6(change status)*/ + else + tmp_mode = tmp_mode & 0xDF;/*clear bit6(change status)*/ + TPD_DEBUG("%s: set TP to mode[0x%x]\n", __func__, tmp_mode); + ret = i2c_smbus_write_byte_data(ts_g->client, F01_RMI_CTRL00, tmp_mode); + if (ret < 0) + TPD_ERR("%s: set dose mode[0x%x] err!!\n", __func__, tmp_mode); + return ret; +} +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + static int gesture_flag; + + struct synaptics_ts_data *ts = + container_of(self, struct synaptics_ts_data, fb_notif); + + if (FB_EARLY_EVENT_BLANK != event && FB_EVENT_BLANK != event) + return 0; + if ((evdata) && (evdata->data) && (ts) && (ts->client)) { + blank = evdata->data; + TPD_DEBUG("%s blank[%d],event[0x%lx]\n", + __func__, *blank, event); + + if ((*blank == FB_BLANK_UNBLANK) + && (event == FB_EARLY_EVENT_BLANK)) { + if (gesture_flag == 1) { + ts->gesture_enable = 0; + DouTap_gesture = 0; + synaptics_enable_interrupt_for_gesture(ts, 0); + set_doze_time(1); + gesture_flag = 0; + } else if (gesture_flag == 2) { + DouTap_gesture = 0; + gesture_flag = 0; + } + if (ts->is_suspended == 1) { + TPD_DEBUG("%s going TP resume start\n", + __func__); + ts->is_suspended = 0; + queue_delayed_work(get_base_report, + &ts->base_work, msecs_to_jiffies(80)); + synaptics_ts_resume(&ts->client->dev); + /*atomic_set(&ts->is_stop,0);*/ + TPD_DEBUG("%s going TP resume end\n", __func__); + } + } else if (*blank == FB_BLANK_NORMAL) { + if (ts->gesture_enable == 0) { + DouTap_gesture = 1; + ts->gesture_enable = 1; + i2c_smbus_write_byte_data(ts->client, + 0xff, 0x0); + synaptics_mode_change(0x80); + synaptics_ts_suspend(&ts->client->dev); + gesture_flag = 1; + } else if ((ts->gesture_enable == 1) && + (DouTap_gesture == 0)) { + DouTap_gesture = 1; + gesture_flag = 2; + } + } else if (*blank == FB_BLANK_POWERDOWN && + (event == FB_EARLY_EVENT_BLANK)) { + if (gesture_flag == 1) { + ts->gesture_enable = 0; + DouTap_gesture = 0; + synaptics_enable_interrupt_for_gesture(ts, 0); + set_doze_time(1); + ts->is_suspended = 0; + gesture_flag = 0; + } else if (gesture_flag == 2) { + DouTap_gesture = 0; + ts->is_suspended = 0; + gesture_flag = 0; + } + if (ts->is_suspended == 0) { + TPD_DEBUG("%s : going TP suspend start\n", + __func__); + ts->is_suspended = 1; + atomic_set(&ts->is_stop, 1); + if (!(ts->gesture_enable)) + touch_disable(ts); + synaptics_ts_suspend(&ts->client->dev); + TPD_DEBUG("%s : going TP suspend end\n", + __func__); + } + } + } + return 0; +} +#endif + +static int __init tpd_driver_init(void) +{ + TPD_ERR("%s enter\n", __func__); + if (i2c_add_driver(&tpd_i2c_driver) != 0) { + TPD_ERR("unable to add i2c driver.\n"); + return -EINVAL; + } + return 0; +} + +/* should never be called */ +static void __exit tpd_driver_exit(void) +{ + i2c_del_driver(&tpd_i2c_driver); + if (synaptics_wq) { + destroy_workqueue(synaptics_wq); + synaptics_wq = NULL; + } +} + +module_init(tpd_driver_init); +module_exit(tpd_driver_exit); + +MODULE_DESCRIPTION("Synaptics S3203 Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/synaptics_redremote.h b/drivers/input/touchscreen/synaptics_redremote.h new file mode 100755 index 000000000000..dfbb72df83dc --- /dev/null +++ b/drivers/input/touchscreen/synaptics_redremote.h @@ -0,0 +1,15 @@ +#ifndef _SYNAPTICS_REDREMOTE_H_ +#define _SYNAPTICS_REDREMOTE_H_ +struct remotepanel_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct input_dev *kpd; + struct mutex *pmutex; + int irq_gpio; + unsigned int irq; + int *enable_remote; +}; +struct remotepanel_data *remote_alloc_panel_data(void); +int register_remote_device(struct remotepanel_data *pdata); +void unregister_remote_device(void); +#endif diff --git a/drivers/input/touchscreen/synaptics_s3320_redremote.c b/drivers/input/touchscreen/synaptics_s3320_redremote.c new file mode 100755 index 000000000000..fe683caef383 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_s3320_redremote.c @@ -0,0 +1,938 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_redremote.h" + + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define DEV_NUMBER 1 +#define REG_ADDR_LIMIT 0xFFFF + + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static int remote_rmi4_i2c_read(unsigned short addr, + unsigned char *data, unsigned short length); +static int remote_rmi4_i2c_write(unsigned short addr, + unsigned char *data, unsigned short length); +static int remote_rmi4_i2c_enable(bool enable); +static int remote_rmi4_get_irq_gpio(void); +static int remote_rmit_set_page(unsigned int address); +static int remote_rmit_put_page(void); + + +static struct input_dev *remote_rmi4_get_input(void); +static struct i2c_client *remote_rmi4_get_i2c_client(void); +static void remote_rmi4_delay_work(struct work_struct *work); +static struct remotepanel_data *remote_free_panel_data + (struct remotepanel_data *pdata); + + +#define MASK_8BIT 0xFF +#define SYN_I2C_RETRY_TIMES 3 +#define BUFFER_SIZE 252 +struct rmidev_handle { + dev_t dev_no; + unsigned short address; + unsigned int length; + struct device dev; + struct kobject *sysfs_dir; + void *data; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; + struct remotepanel_data *pdata; +}; + +static struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUSR | S_IWUSR), + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static struct device_attribute attrs[] = { + __ATTR(open, S_IRUSR | S_IWUSR, + NULL, + rmidev_sysfs_open_store), + __ATTR(release, S_IRUSR | S_IWUSR, + NULL, + rmidev_sysfs_release_store), + __ATTR(address, S_IRUSR | S_IWUSR, + NULL, + rmidev_sysfs_address_store), + __ATTR(length, S_IRUSR | S_IWUSR, + NULL, + rmidev_sysfs_length_store), + __ATTR(attn_state, S_IRUSR | S_IWUSR, + rmidev_sysfs_attn_state_show, + NULL), +}; + +static int rmidev_major_num; + +static struct class *rmidev_device_class; + +static struct rmidev_handle *rmidev; + + +static struct device *device_ptr; +static struct delayed_work delay_work; + + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int data_length = rmidev->length; + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (count < data_length) { + dev_err(device_ptr, + "%s: Not enough space (%zd bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + if (data_length) { + retval = remote_rmi4_i2c_read( + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(device_ptr, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int data_length = rmidev->length; + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = remote_rmi4_i2c_write( + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(device_ptr, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + remote_rmi4_i2c_enable(false); + dev_dbg(device_ptr, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + remote_rmi4_i2c_enable(true); + dev_dbg(device_ptr, + "%s: Attention interrupt enabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int attn_state; + + attn_state = gpio_get_value(remote_rmi4_get_irq_gpio()); + + return snprintf(buf, PAGE_SIZE, "%d\n", attn_state); +} + +static int remote_rmi4_get_irq_gpio(void) +{ + struct rmidev_data *dev_data = (struct rmidev_data *)rmidev->data; + + return dev_data->pdata->irq_gpio; +} + +static struct input_dev *remote_rmi4_get_input(void) +{ + struct rmidev_data *dev_data = (struct rmidev_data *)rmidev->data; + + return dev_data->pdata->input_dev; +} + +static struct i2c_client *remote_rmi4_get_i2c_client(void) +{ + struct rmidev_data *dev_data = (struct rmidev_data *)rmidev->data; + + return dev_data->pdata->client; +} + +static int remote_rmit_set_page(unsigned int address) +{ + struct i2c_client *i2c_client = remote_rmi4_get_i2c_client(); + unsigned char retry; + unsigned char buf[2]; + struct i2c_msg msg[] = { + { + .addr = i2c_client->addr, + .flags = 0, + .len = 2, + .buf = buf, + } + }; + buf[0] = 0xff; + buf[1] = ((address >> 8) & 0xFF); + + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(i2c_client->adapter, msg, 1) == 1) + break; + msleep(20); + } + + if (retry == 2) + return -EIO; + + return 0; +} + +static int remote_rmit_put_page(void) +{ + struct i2c_client *i2c_client = remote_rmi4_get_i2c_client(); + unsigned char retry; + unsigned char buf[2]; + struct i2c_msg msg[] = { + { + .addr = i2c_client->addr, + .flags = 0, + .len = 2, + .buf = buf, + } + }; + buf[0] = 0xff; + buf[1] = 0x00; + + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(i2c_client->adapter, msg, 1) == 1) + break; + msleep(20); + } + + if (retry == 2) + return -EIO; + + return 0; +} + + +int remote_rmi4_i2c_read(unsigned short addr, + unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf; + struct i2c_client *i2c_client = remote_rmi4_get_i2c_client(); + struct i2c_msg msg[] = { + { + .addr = i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & 0xff; + + retval = remote_rmit_set_page(addr); + if (retval < 0) + goto exit; + + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + msleep(20); + } + + if (retry == 2) + retval = -EIO; + +exit: + remote_rmit_put_page(); + + return retval; +} + +int remote_rmi4_i2c_write(unsigned short addr, + unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char buf[length + 1]; + struct i2c_client *i2c_client = remote_rmi4_get_i2c_client(); + struct i2c_msg msg[] = { + { + .addr = i2c_client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + + retval = remote_rmit_set_page(addr); + if (retval < 0) + goto exit; + + buf[0] = addr & 0xff; + memcpy(&buf[1], &data[0], length); + + for (retry = 0; retry < 2; retry++) { + if (i2c_transfer(i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + msleep(20); + } + msleep(20); + if (retry == 2) + retval = -EIO; + +exit: + remote_rmit_put_page(); + + return retval; +} + +int remote_rmi4_i2c_enable(bool enable) +{ + struct rmidev_data *dev_data = (struct rmidev_data *)rmidev->data; + + if (enable) + *(dev_data->pdata->enable_remote) = 0; + else + *(dev_data->pdata->enable_remote) = 1; + + return 0; +} + + +/* + * rmidev_llseek - used to set up register address + * + * @filp: file structure for seek + * @off: offset + * if whence == SEEK_SET, + * high 16 bits: page address + * low 16 bits: register address + * if whence == SEEK_CUR, + * offset from current position + * if whence == SEEK_END, + * offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(device_ptr, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: - use to read data from rmi device + * + * @filp: file structure for read + * @buf: user space buffer pointer + * @count: number of bytes to read + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char tmpbuf[count + 1]; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + + if (count == 0) + return 0; + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + mutex_lock(dev_data->pdata->pmutex); + mutex_lock(&(dev_data->file_mutex)); + + retval = remote_rmi4_i2c_read( + *f_pos, + tmpbuf, + count); + if (retval < 0) + goto clean_up; + + if (copy_to_user(buf, tmpbuf, count)) + retval = -EFAULT; + else + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + mutex_unlock(dev_data->pdata->pmutex); + + return retval; +} + +/* + * rmidev_write: - used to write data to rmi device + * + * @filep: file structure for write + * @buf: user space buffer pointer + * @count: number of bytes to write + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char tmpbuf[count + 1]; + struct rmidev_data *dev_data = filp->private_data; + + pr_err("synap %s\n", __func__); + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + + if (count == 0) + return 0; + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + mutex_lock(dev_data->pdata->pmutex); + mutex_lock(&(dev_data->file_mutex)); + + retval = remote_rmi4_i2c_write( + *f_pos, + tmpbuf, + count); + if (retval >= 0) + *f_pos += retval; + + + mutex_unlock(&(dev_data->file_mutex)); + mutex_unlock(dev_data->pdata->pmutex); + + return retval; +} + +static int rmidev_create_attr(bool create) +{ + int retval = 0; + unsigned char attr_count; + struct input_dev *input_dev = remote_rmi4_get_input(); + + pr_err("synap %s\n", __func__); + if (!create) + goto err_sysfs_attrs; + + if (rmidev->sysfs_dir) + return 0; + + if (!input_dev) + return -ENOMEM; + + rmidev->sysfs_dir = kobject_create_and_add("rmidev", + &input_dev->dev.kobj); + if (!rmidev->sysfs_dir) { + dev_err(device_ptr, + "%s: Failed to create sysfs directory\n", + __func__); + return -ENOMEM; + } + + + retval = sysfs_create_bin_file(rmidev->sysfs_dir, + &attr_data); + if (retval < 0) { + dev_err(device_ptr, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(rmidev->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(device_ptr, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + if (!rmidev->sysfs_dir) + return 0; + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); + +err_sysfs_bin: + kobject_put(rmidev->sysfs_dir); + rmidev->sysfs_dir = NULL; + + return retval; +} + +/* + * rmidev_open: enable access to rmi device + * @inp: inode struture + * @filp: file structure + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + pr_err("synap %s\n", __func__); + + if (!dev_data) + return -EACCES; + + + rmidev_create_attr(true); + + filp->private_data = dev_data; + + mutex_lock(&(dev_data->file_mutex)); + *(dev_data->pdata->enable_remote) = 1; + /*remote_rmi4_i2c_enable(false);*/ + dev_dbg(device_ptr, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_release: - release access to rmi device + * @inp: inode structure + * @filp: file structure + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + rmidev_create_attr(false); + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + remote_rmi4_i2c_enable(true); + dev_dbg(device_ptr, + "%s: Attention interrupt enabled\n", + __func__); + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + dev_t devno; + + if (dev_data) { + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + unregister_chrdev_region(devno, 1); + + remote_free_panel_data(dev_data->pdata); + + dev_dbg(device_ptr, + "%s: rmidev device removed\n", + __func__); + } +} + +static char *rmi_char_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static int rmidev_create_device_class(void) +{ + rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); + + if (IS_ERR(rmidev_device_class)) { + pr_err("%s: Failed to create /dev/%s\n", + __func__, CHAR_DEVICE_NAME); + return -ENODEV; + } + + rmidev_device_class->devnode = rmi_char_devnode; + + return 0; +} + +static void remote_rmi4_delay_work(struct work_struct *work) +{ + rmidev_create_attr(true); +} + +struct remotepanel_data *remote_alloc_panel_data(void) +{ + if (rmidev) { + pr_err("%s:remote panel data has alloc already null\n", + __func__); + return NULL; + } + + return kzalloc(sizeof(struct remotepanel_data), GFP_KERNEL); +} + +static struct remotepanel_data *remote_free_panel_data + (struct remotepanel_data *pdata) +{ + pdata = NULL; + return NULL; +} + + + +/*int rmidev_init_device(void)*/ +int register_remote_device(struct remotepanel_data *pdata) +{ + int retval; + dev_t dev_no; + struct rmidev_data *dev_data = NULL; + + if (pdata == NULL) { + pr_err("%s:pdata is null\n", __func__); + return -ENOMEM; + } + if (rmidev) { + pr_err("%s:remote device has register already null\n", + __func__); + return -ENOMEM; + } + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + retval = -ENOMEM; + goto err_rmidev; + } + + retval = rmidev_create_device_class(); + if (retval < 0) + goto err_device_class; + + if (rmidev_major_num) { + dev_no = MKDEV(rmidev_major_num, DEV_NUMBER); + retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + retval = alloc_chrdev_region(&dev_no, 1, 1, CHAR_DEVICE_NAME); + if (retval < 0) + goto err_device_region; + + rmidev_major_num = MAJOR(dev_no); + } + + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + retval = -ENOMEM; + goto err_dev_data; + } + + dev_data->pdata = pdata; + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) + goto err_char_device; + + dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no)); + dev_data->device_class = rmidev_device_class; + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + pr_err("%s: Failed to create rmi char device\n", __func__); + retval = -ENODEV; + goto err_char_device; + } + + INIT_DELAYED_WORK(&delay_work, remote_rmi4_delay_work); + schedule_delayed_work(&delay_work, msecs_to_jiffies(8*1000)); + + return 0; + +err_char_device: + remote_free_panel_data(dev_data->pdata); + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, 1); + +err_device_region: + class_destroy(rmidev_device_class); + +err_device_class: + kfree(rmidev); + rmidev = NULL; +err_rmidev: + return retval; +} + +/*void rmidev_remove_device(void)*/ +void unregister_remote_device(void) +{ + struct rmidev_data *dev_data; + + if (!rmidev) + return; + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + class_destroy(rmidev_device_class); + + kfree(rmidev); +} diff --git a/drivers/iommu/msm_dma_iommu_mapping.c b/drivers/iommu/msm_dma_iommu_mapping.c index 25fe36ab6339..1ef527d68553 100644 --- a/drivers/iommu/msm_dma_iommu_mapping.c +++ b/drivers/iommu/msm_dma_iommu_mapping.c @@ -206,10 +206,16 @@ static inline int __msm_dma_map_sg(struct device *dev, struct scatterlist *sg, kref_init(&iommu_map->ref); if (late_unmap) kref_get(&iommu_map->ref); + iommu_map->meta = iommu_meta; iommu_map->sgl.dma_address = sg->dma_address; iommu_map->sgl.dma_length = sg->dma_length; iommu_map->dev = dev; + iommu_map->dir = dir; + iommu_map->nents = nents; + iommu_map->sgl.page_link = sg->page_link; + iommu_map->sgl.offset = sg->offset; + iommu_map->sgl.length = sg->length; msm_iommu_add(iommu_meta, iommu_map); } else { diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 2b8b0e4036b5..1ea75d4f487e 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -37,6 +37,7 @@ #include #include "irq-gic-common.h" +#include struct redist_region { void __iomem *redist_base; @@ -441,7 +442,7 @@ static void gic_show_resume_irq(struct gic_chip_data *gic) name = "stray irq"; else if (desc->action && desc->action->name) name = desc->action->name; - + log_wakeup_reason(irq); pr_warn("%s: %d triggered %s\n", __func__, irq, name); } } diff --git a/drivers/irqchip/msm_show_resume_irq.c b/drivers/irqchip/msm_show_resume_irq.c index 521149671fdf..cfadb7c2643d 100644 --- a/drivers/irqchip/msm_show_resume_irq.c +++ b/drivers/irqchip/msm_show_resume_irq.c @@ -15,7 +15,7 @@ #include #include -int msm_show_resume_irq_mask; +int msm_show_resume_irq_mask = 1; module_param_named( debug_mask, msm_show_resume_irq_mask, int, S_IRUGO | S_IWUSR | S_IWGRP diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c old mode 100644 new mode 100755 index 5db4515a4fd7..6ab35b8f925f --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -21,7 +21,7 @@ #include #include #include - +#include struct gpio_led_data { struct led_classdev cdev; struct gpio_desc *gpiod; @@ -31,6 +31,9 @@ struct gpio_led_data { u8 blinking; int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state, unsigned long *delay_on, unsigned long *delay_off); + struct regulator *vdd; + int vmin_high; + int vmin_low; }; static void gpio_led_work(struct work_struct *work) @@ -58,6 +61,10 @@ static void gpio_led_set(struct led_classdev *led_cdev, else level = 1; + if(led_dat->vdd){ + regulator_set_voltage(led_dat->vdd, level?led_dat->vmin_high:led_dat->vmin_low,INT_MAX); + } + /* Setting GPIOs with I2C/etc requires a task context, and we don't * seem to have a reliable way to know if we're already in one; so * let's just assume the worst. @@ -92,7 +99,7 @@ static int create_gpio_led(const struct gpio_led *template, unsigned long *)) { int ret, state; - + const __be32 *min_uV, *max_uV; led_dat->gpiod = template->gpiod; if (!led_dat->gpiod) { /* @@ -126,6 +133,15 @@ static int create_gpio_led(const struct gpio_led *template, led_dat->cdev.default_trigger = template->default_trigger; led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod); led_dat->blinking = 0; + + led_dat->vdd = regulator_get(parent, "vdd"); + min_uV = of_get_property(parent->of_node, "keypad-led-vbob-min", NULL); + max_uV = of_get_property(parent->of_node, "keypad-led-vbob-max", NULL); + if (!IS_ERR(led_dat->vdd)) { + led_dat->vmin_low = be32_to_cpu(*min_uV);; + led_dat->vmin_high = be32_to_cpu(*max_uV);; + } + if (blink_set) { led_dat->platform_gpio_blink_set = blink_set; led_dat->cdev.blink_set = gpio_blink_set; @@ -250,7 +266,8 @@ static int gpio_led_probe(struct platform_device *pdev) struct gpio_leds_priv *priv; int i, ret = 0; - if (pdata && pdata->num_leds) { + if (pdata) { + if (pdata->num_leds) { priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(pdata->num_leds), GFP_KERNEL); @@ -268,8 +285,10 @@ static int gpio_led_probe(struct platform_device *pdev) delete_gpio_led(&priv->leds[i]); return ret; } - } - } else { + } + } + } + else { priv = gpio_leds_create(pdev); if (IS_ERR(priv)) return PTR_ERR(priv); diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c old mode 100644 new mode 100755 index e3cfffb5c563..7fc889562c83 --- a/drivers/leds/leds-qpnp.c +++ b/drivers/leds/leds-qpnp.c @@ -251,6 +251,8 @@ #define NUM_KPDBL_LEDS 4 #define KPDBL_MASTER_BIT_INDEX 0 +static u8 shutdown_enable = 0; + /** * enum qpnp_leds - QPNP supported led ids * @QPNP_ID_WLED - White led backlight @@ -2550,11 +2552,12 @@ static ssize_t duty_pcts_store(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); char *buffer; ssize_t ret; - int i = 0; + int rets; + //int i = 0; int max_duty_pcts; struct pwm_config_data *pwm_cfg; u32 previous_num_duty_pcts; - int value; + //int value; int *previous_duty_pcts; led = container_of(led_cdev, struct qpnp_led_data, cdev); @@ -2585,16 +2588,24 @@ static ssize_t duty_pcts_store(struct device *dev, buffer = (char *)buf; - for (i = 0; i < max_duty_pcts; i++) { - if (buffer == NULL) - break; - ret = sscanf((const char *)buffer, "%u,%s", &value, buffer); - pwm_cfg->old_duty_pcts[i] = value; - num_duty_pcts++; - if (ret <= 1) - break; + rets= sscanf((const char *)buffer, + "bean %x %x %x %x %x %x %x %x %x %x %x ", + &pwm_cfg->old_duty_pcts[0], &pwm_cfg->old_duty_pcts[1], + &pwm_cfg->old_duty_pcts[2], &pwm_cfg->old_duty_pcts[3], + &pwm_cfg->old_duty_pcts[4], &pwm_cfg->old_duty_pcts[5], + &pwm_cfg->old_duty_pcts[6],&pwm_cfg->old_duty_pcts[7], + &pwm_cfg->old_duty_pcts[8], &pwm_cfg->old_duty_pcts[9], + &pwm_cfg->old_duty_pcts[10]); + printk("bean set rets = %d\n",rets); + if(rets != 11) + { + pr_err("duty_pcts_store: Invalid paramter:%d\n", rets); + return -1; } + num_duty_pcts = 11; + + if (num_duty_pcts >= max_duty_pcts) { dev_err(&led->pdev->dev, "Number of duty pcts given exceeds max (%d)\n", @@ -2722,6 +2733,42 @@ static ssize_t blink_store(struct device *dev, return count; } +static ssize_t shutdown_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", shutdown_enable); +} + +static ssize_t shutdown_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + if (count < 1) + return -EINVAL; + + switch (buf[0]) { + case '0': + shutdown_enable = 0; + break; + case '3': + shutdown_enable = 3; + break; + case '4': + shutdown_enable = 4; + break; + case '5': + shutdown_enable = 5; + break; + default: + return -EINVAL; + } + + return count; + +} + static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store); static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store); static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store); @@ -2732,7 +2779,7 @@ static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store); static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store); static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store); static DEVICE_ATTR(blink, 0664, NULL, blink_store); - +static DEVICE_ATTR(enable, 0644, shutdown_enable_show, shutdown_enable_store); static struct attribute *led_attrs[] = { &dev_attr_led_mode.attr, &dev_attr_strobe.attr, @@ -2745,6 +2792,7 @@ static const struct attribute_group led_attr_group = { static struct attribute *pwm_attrs[] = { &dev_attr_pwm_us.attr, + &dev_attr_enable.attr, NULL }; @@ -4238,6 +4286,37 @@ static int qpnp_leds_remove(struct platform_device *pdev) return 0; } +static void qpnp_leds_shutdown(struct platform_device *pdev) +{ + struct qpnp_led_data *led_array = dev_get_drvdata(&pdev->dev); + int i, parsed_leds = led_array->num_leds; + + for (i = 0; i < parsed_leds; i++) { + if(led_array[i].id == QPNP_ID_RGB_RED){ + if(shutdown_enable == QPNP_ID_RGB_RED) + led_array[i].cdev.brightness = LED_FULL; + else + led_array[i].cdev.brightness = LED_OFF; + } + else if(led_array[i].id == QPNP_ID_RGB_GREEN){ + if(shutdown_enable == QPNP_ID_RGB_GREEN) + led_array[i].cdev.brightness = LED_FULL; + else + led_array[i].cdev.brightness = LED_OFF; + } + else if(led_array[i].id == QPNP_ID_RGB_BLUE){ + if(shutdown_enable == QPNP_ID_RGB_BLUE) + led_array[i].cdev.brightness = LED_FULL; + else + led_array[i].cdev.brightness = LED_OFF; + } + else + led_array[i].cdev.brightness = LED_OFF; + + __qpnp_led_work(led_array+i, led_array[i].cdev.brightness); + } +} + #ifdef CONFIG_OF static const struct of_device_id spmi_match_table[] = { { .compatible = "qcom,leds-qpnp",}, @@ -4254,6 +4333,7 @@ static struct platform_driver qpnp_leds_driver = { }, .probe = qpnp_leds_probe, .remove = qpnp_leds_remove, + .shutdown = qpnp_leds_shutdown, }; static int __init qpnp_led_init(void) diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c index ccdd4622c120..c494ef720b9b 100644 --- a/drivers/media/platform/msm/camera_v2/camera/camera.c +++ b/drivers/media/platform/msm/camera_v2/camera/camera.c @@ -576,10 +576,10 @@ static int camera_v4l2_fh_release(struct file *filep) if (sp) { v4l2_fh_del(&sp->fh); v4l2_fh_exit(&sp->fh); + mutex_destroy(&sp->lock); + kzfree(sp); } - mutex_destroy(&sp->lock); - kzfree(sp); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c index ae3f7e89a8b4..79231fb314ad 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, 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 @@ -25,8 +25,8 @@ #define MSM_CAMERA_TZ_BOOT_PROTECTED (false) /* Update version major number in case the HLOS-TA interface is changed*/ -#define TA_IF_VERSION_MAJ 2 -#define TA_IF_VERSION_MIN 1 +#define TA_IF_VERSION_MAJ 1 +#define TA_IF_VERSION_MIN 2 #undef CDBG #ifdef MSM_CAMERA_TZ_UTIL_VERBOSE diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h index eaa77db3957c..146b7d029984 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,7 +29,7 @@ #define MSM_CAMERA_TZ_HW_BLOCK_CPP 0x0000000010 enum msm_camera_tz_cmd_id_t { - MSM_CAMERA_TZ_CMD_NONE = 56000, + MSM_CAMERA_TZ_CMD_NONE, MSM_CAMERA_TZ_CMD_GET_IF_VERSION, MSM_CAMERA_TZ_CMD_POWER_UP, MSM_CAMERA_TZ_CMD_POWER_DOWN, diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index 8e0a7443f98c..d881b4aea48f 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2018, 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 @@ -442,7 +442,7 @@ static int msm_fd_open(struct file *file) } ctx->mem_pool.fd_device = ctx->fd_device; - ctx->stats = vzalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); + ctx->stats = vmalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); if (!ctx->stats) { dev_err(device->dev, "No memory for face statistics\n"); ret = -ENOMEM; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 20a38925aa10..eab56b70e646 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -26,7 +26,6 @@ #include #include #include -#include #include "msm.h" #include "msm_buf_mgr.h" diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c index 18961e69aadc..3bda6dd9f2d9 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -53,6 +53,7 @@ MODULE_DEVICE_TABLE(of, msm_vfe_dt_match); #define MAX_OVERFLOW_COUNTERS 29 #define OVERFLOW_LENGTH 1024 #define OVERFLOW_BUFFER_LENGTH 64 +static char stat_line[OVERFLOW_LENGTH]; struct msm_isp_statistics stats; struct msm_isp_ub_info ub_info; @@ -112,30 +113,19 @@ static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file) return 0; } -static ssize_t vfe_debugfs_statistics_read(struct file *t_file, - char __user *t_char, size_t t_size_t, loff_t *t_loff_t) +static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char, + size_t t_size_t, loff_t *t_loff_t) { int i; - size_t rc; uint64_t *ptr; char buffer[OVERFLOW_BUFFER_LENGTH] = {0}; - char *stat_line; struct vfe_device *vfe_dev = (struct vfe_device *) t_file->private_data; - struct msm_isp_statistics *stats; + struct msm_isp_statistics *stats = vfe_dev->stats; - stat_line = kzalloc(OVERFLOW_LENGTH, GFP_KERNEL); - if (!stat_line) - return -ENOMEM; - spin_lock(&vfe_dev->common_data->common_dev_data_lock); - stats = vfe_dev->stats; + memset(stat_line, 0, sizeof(stat_line)); msm_isp_util_get_bandwidth_stats(vfe_dev, stats); - spin_unlock(&vfe_dev->common_data->common_dev_data_lock); ptr = (uint64_t *)(stats); - if (MAX_OVERFLOW_COUNTERS > OVERFLOW_LENGTH) { - kfree(stat_line); - return -EINVAL; - } for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) { strlcat(stat_line, stats_str[i], sizeof(stat_line)); strlcat(stat_line, " ", sizeof(stat_line)); @@ -143,10 +133,8 @@ static ssize_t vfe_debugfs_statistics_read(struct file *t_file, strlcat(stat_line, buffer, sizeof(stat_line)); strlcat(stat_line, "\r\n", sizeof(stat_line)); } - rc = simple_read_from_buffer(t_char, t_size_t, + return simple_read_from_buffer(t_char, t_size_t, t_loff_t, stat_line, strlen(stat_line)); - kfree(stat_line); - return rc; } static ssize_t vfe_debugfs_statistics_write(struct file *t_file, @@ -154,12 +142,8 @@ static ssize_t vfe_debugfs_statistics_write(struct file *t_file, { struct vfe_device *vfe_dev = (struct vfe_device *) t_file->private_data; - struct msm_isp_statistics *stats; - - spin_lock(&vfe_dev->common_data->common_dev_data_lock); - stats = vfe_dev->stats; + struct msm_isp_statistics *stats = vfe_dev->stats; memset(stats, 0, sizeof(struct msm_isp_statistics)); - spin_unlock(&vfe_dev->common_data->common_dev_data_lock); return sizeof(struct msm_isp_statistics); } @@ -712,8 +696,7 @@ int vfe_hw_probe(struct platform_device *pdev) spin_lock_init(&vfe_dev->shared_data_lock); spin_lock_init(&vfe_dev->reg_update_lock); spin_lock_init(&req_history_lock); - spin_lock_init(&vfe_dev->reset_completion_lock); - spin_lock_init(&vfe_dev->halt_completion_lock); + spin_lock_init(&vfe_dev->completion_lock); media_entity_init(&vfe_dev->subdev.sd.entity, 0, NULL, 0); vfe_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index da8fbb3cd5b9..d336e1ef1bd7 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -155,11 +155,9 @@ struct msm_vfe_irq_ops { struct msm_isp_timestamp *ts); void (*process_axi_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts); void (*process_stats_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts); void (*config_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, @@ -598,7 +596,6 @@ struct msm_vfe_tasklet_queue_cmd { struct list_head list; uint32_t vfeInterruptStatus0; uint32_t vfeInterruptStatus1; - uint32_t vfe_pingpong_status; struct msm_isp_timestamp ts; uint8_t cmd_used; struct vfe_device *vfe_dev; @@ -804,8 +801,7 @@ struct vfe_device { struct mutex core_mutex; spinlock_t shared_data_lock; spinlock_t reg_update_lock; - spinlock_t reset_completion_lock; - spinlock_t halt_completion_lock; + spinlock_t completion_lock; /* Tasklet info */ atomic_t irq_cnt; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index f4e4ca6cb6dc..52269339d8bb 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -35,13 +35,12 @@ #define VFE40_STATS_BURST_LEN_8916_VERSION 2 #define VFE40_FETCH_BURST_LEN 3 #define VFE40_UB_SIZE 1536 /* 1536 * 128 bits = 24KB */ -#define VFE40_STATS_SIZE 392 #define VFE40_UB_SIZE_8952 2048 /* 2048 * 128 bits = 32KB */ #define VFE40_UB_SIZE_8916 3072 /* 3072 * 128 bits = 48KB */ #define VFE40_EQUAL_SLICE_UB 190 /* (UB_SIZE - STATS SIZE)/6 */ #define VFE40_EQUAL_SLICE_UB_8916 236 #define VFE40_TOTAL_WM_UB 1144 /* UB_SIZE - STATS SIZE */ -#define VFE40_TOTAL_WM_UB_8916 2680 +#define VFE40_TOTAL_WM_UB_8916 1656 #define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx) #define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx) #define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2)) @@ -105,11 +104,7 @@ static uint32_t msm_vfe40_ub_reg_offset(struct vfe_device *vfe_dev, int idx) static uint32_t msm_vfe40_get_ub_size(struct vfe_device *vfe_dev) { - if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION || - vfe_dev->vfe_hw_version == VFE40_8939_VERSION || - vfe_dev->vfe_hw_version == VFE40_8937_VERSION || - vfe_dev->vfe_hw_version == VFE40_8953_VERSION || - vfe_dev->vfe_hw_version == VFE40_8917_VERSION) { + if (vfe_dev->vfe_hw_version == VFE40_8916_VERSION) { vfe_dev->ub_info->wm_ub = VFE40_TOTAL_WM_UB_8916; return VFE40_TOTAL_WM_UB_8916; } @@ -370,24 +365,15 @@ static void msm_vfe40_clear_status_reg(struct vfe_device *vfe_dev) static void msm_vfe40_process_reset_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1) { - unsigned long flags; - - if (irq_status0 & (1 << 31)) { - spin_lock_irqsave(&vfe_dev->reset_completion_lock, flags); + if (irq_status0 & (1 << 31)) complete(&vfe_dev->reset_complete); - spin_unlock_irqrestore(&vfe_dev->reset_completion_lock, flags); - } } static void msm_vfe40_process_halt_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1) { - unsigned long flags; - if (irq_status1 & (1 << 8)) { - spin_lock_irqsave(&vfe_dev->halt_completion_lock, flags); complete(&vfe_dev->halt_complete); - spin_unlock_irqrestore(&vfe_dev->halt_completion_lock, flags); msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x2C0); } } @@ -600,11 +586,6 @@ static void msm_vfe40_read_and_clear_irq_status(struct vfe_device *vfe_dev, *irq_status0 &= vfe_dev->irq0_mask; *irq_status1 &= vfe_dev->irq1_mask; - if (*irq_status0 && - (*irq_status0 == msm_camera_io_r(vfe_dev->vfe_base + 0x38))) { - msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30); - msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24); - } if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = @@ -776,11 +757,7 @@ static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev, uint32_t first_start, uint32_t blocking_call) { long rc = 0; - unsigned long flags; - - spin_lock_irqsave(&vfe_dev->reset_completion_lock, flags); init_completion(&vfe_dev->reset_complete); - spin_unlock_irqrestore(&vfe_dev->reset_completion_lock, flags); if (first_start) { msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC); @@ -1074,18 +1051,15 @@ static int msm_vfe40_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1135,18 +1109,15 @@ static int msm_vfe40_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1793,7 +1764,6 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev, int rc = 0; enum msm_vfe_input_src i; struct msm_isp_timestamp ts; - unsigned long flags; /* Keep only halt and restart mask */ msm_vfe40_config_irq(vfe_dev, (1 << 31), (1 << 8), @@ -1810,9 +1780,7 @@ static int msm_vfe40_axi_halt(struct vfe_device *vfe_dev, msm_isp_stats_stream_update(vfe_dev); if (blocking) { - spin_lock_irqsave(&vfe_dev->halt_completion_lock, flags); init_completion(&vfe_dev->halt_complete); - spin_unlock_irqrestore(&vfe_dev->halt_completion_lock, flags); /* Halt AXI Bus Bridge */ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0); rc = wait_for_completion_interruptible_timeout( @@ -2236,7 +2204,7 @@ static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = { .num_comp_mask = 3, .num_rdi = 3, .num_rdi_master = 3, - .min_wm_ub = 96, + .min_wm_ub = 64, .scratch_buf_range = SZ_32M + SZ_4M, }; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index 3b8de1a13c88..cc4dd5eaf93e 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -891,18 +891,14 @@ static int msm_vfe44_fetch_engine_start(struct vfe_device *vfe_dev, vfe_dev->buf_mgr, fe_cfg->session_id, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0) { pr_err("%s: No fetch buffer\n", __func__); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index f8866b01e617..632624034a04 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -833,18 +833,15 @@ static int msm_vfe46_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 0daf2d914be5..360121e89260 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -443,10 +443,10 @@ void msm_vfe47_process_reset_irq(struct vfe_device *vfe_dev, unsigned long flags; if (irq_status0 & (1 << 31)) { - spin_lock_irqsave(&vfe_dev->reset_completion_lock, flags); + spin_lock_irqsave(&vfe_dev->completion_lock, flags); complete(&vfe_dev->reset_complete); vfe_dev->reset_pending = 0; - spin_unlock_irqrestore(&vfe_dev->reset_completion_lock, flags); + spin_unlock_irqrestore(&vfe_dev->completion_lock, flags); } } @@ -454,12 +454,9 @@ void msm_vfe47_process_halt_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1) { uint32_t val = 0; - unsigned long flags; if (irq_status1 & (1 << 8)) { - spin_lock_irqsave(&vfe_dev->halt_completion_lock, flags); complete(&vfe_dev->halt_complete); - spin_unlock_irqrestore(&vfe_dev->halt_completion_lock, flags); msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x400); } @@ -573,7 +570,6 @@ void msm_vfe47_process_error_status(struct vfe_device *vfe_dev) void msm_vfe47_read_and_clear_irq_status(struct vfe_device *vfe_dev, uint32_t *irq_status0, uint32_t *irq_status1) { - uint32_t count = 0; *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x6C); *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70); /* Mask off bits that are not enabled */ @@ -582,14 +578,6 @@ void msm_vfe47_read_and_clear_irq_status(struct vfe_device *vfe_dev, msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); *irq_status0 &= vfe_dev->irq0_mask; *irq_status1 &= vfe_dev->irq1_mask; - /* check if status register is cleared if not clear again*/ - while (*irq_status0 && - (*irq_status0 & msm_camera_io_r(vfe_dev->vfe_base + 0x6C)) && - (count < MAX_RECOVERY_THRESHOLD)) { - msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x64); - msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); - count++; - } if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = @@ -777,9 +765,9 @@ long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev, uint32_t reset; unsigned long flags; - spin_lock_irqsave(&vfe_dev->reset_completion_lock, flags); + spin_lock_irqsave(&vfe_dev->completion_lock, flags); init_completion(&vfe_dev->reset_complete); - spin_unlock_irqrestore(&vfe_dev->reset_completion_lock, flags); + spin_unlock_irqrestore(&vfe_dev->completion_lock, flags); if (blocking_call) vfe_dev->reset_pending = 1; @@ -1107,18 +1095,15 @@ int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1168,18 +1153,15 @@ int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); - mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; - mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1907,10 +1889,6 @@ void msm_vfe47_cfg_axi_ub_equal_default( stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(axi_data->free_wm[i])); - if (!stream_info) { - pr_err("%s: stream_info is NULL!", __func__); - return; - } vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); for (plane = 0; plane < stream_info->num_planes; @@ -2003,7 +1981,6 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, enum msm_vfe_input_src i; uint32_t val = 0; struct msm_isp_timestamp ts; - unsigned long flags; val = msm_camera_io_r(vfe_dev->vfe_vbif_base + VFE47_VBIF_CLK_OFFSET); val |= 0x1; @@ -2020,9 +1997,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev, __func__, vfe_dev->pdev->id, blocking); if (blocking) { - spin_lock_irqsave(&vfe_dev->halt_completion_lock, flags); init_completion(&vfe_dev->halt_complete); - spin_unlock_irqrestore(&vfe_dev->halt_completion_lock, flags); /* Halt AXI Bus Bridge */ msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400); rc = wait_for_completion_interruptible_timeout( @@ -2600,7 +2575,6 @@ int msm_vfe47_get_clks(struct vfe_device *vfe_dev) if (rc) return rc; - vfe_dev->num_norm_clk = vfe_dev->num_clk; for (i = 0; i < vfe_dev->num_clk; i++) { if (strcmp(vfe_dev->vfe_clk_info[i].clk_name, "camss_vfe_stream_clk") == 0) { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 661850d6d7c6..2e88acff110d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -236,7 +236,6 @@ static int msm_isp_validate_axi_request(struct vfe_device *vfe_dev, case V4L2_PIX_FMT_META: case V4L2_PIX_FMT_META10: case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y10: stream_info->num_planes = 1; stream_info->format_factor = ISP_Q2; break; @@ -346,7 +345,6 @@ static uint32_t msm_isp_axi_get_plane_size( case V4L2_PIX_FMT_QGRBG10: case V4L2_PIX_FMT_QRGGB10: case V4L2_PIX_FMT_META10: - case V4L2_PIX_FMT_Y10: /* TODO: fix me */ size = plane_cfg[plane_idx].output_height * plane_cfg[plane_idx].output_width; @@ -528,12 +526,6 @@ static void msm_isp_cfg_framedrop_reg( enum msm_vfe_input_src frame_src = SRC_TO_INTF(stream_info->stream_src); int i; - if (vfe_dev == NULL) { - pr_err("%s %d returning vfe_dev is NULL\n", - __func__, __LINE__); - return; - } - if (vfe_dev->axi_data.src_info[frame_src].frame_id >= stream_info->init_frame_drop) runtime_init_frame_drop = 0; @@ -1699,40 +1691,12 @@ void msm_isp_halt_send_error(struct vfe_device *vfe_dev, uint32_t event) struct msm_vfe_axi_halt_cmd halt_cmd; struct vfe_device *temp_dev = NULL; uint32_t irq_status0 = 0, irq_status1 = 0; - struct vfe_device *vfe_dev_other = NULL; - uint32_t vfe_id_other = 0; - unsigned long flags; if (atomic_read(&vfe_dev->error_info.overflow_state) != NO_OVERFLOW) /* Recovery is already in Progress */ return; - /* if there are no active streams - do not start recovery */ - if (vfe_dev->is_split) { - if (vfe_dev->pdev->id == ISP_VFE0) - vfe_id_other = ISP_VFE1; - else - vfe_id_other = ISP_VFE0; - - spin_lock_irqsave( - &vfe_dev->common_data->common_dev_data_lock, flags); - vfe_dev_other = vfe_dev->common_data->dual_vfe_res-> - vfe_dev[vfe_id_other]; - if (!vfe_dev->axi_data.num_active_stream || - !vfe_dev_other->axi_data.num_active_stream) { - spin_unlock_irqrestore( - &vfe_dev->common_data->common_dev_data_lock, - flags); - pr_err("%s:skip the recovery as no active streams\n", - __func__); - return; - } - spin_unlock_irqrestore( - &vfe_dev->common_data->common_dev_data_lock, flags); - } else if (!vfe_dev->axi_data.num_active_stream) - return; - if (event == ISP_EVENT_PING_PONG_MISMATCH && vfe_dev->axi_data.recovery_count < MAX_RECOVERY_THRESHOLD) { pr_err("%s: ping pong mismatch on vfe%d recovery count %d\n", @@ -2519,12 +2483,18 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) rc = -1; return rc; } - if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { +/* if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { pr_err("%s: ab_ib_vote num_src is exceeding limit\n", __func__); rc = -1; return rc; } + if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { + pr_err("%s: ab_ib_vote num_src is exceeding limit\n", + __func__); + rc = -1; + return rc; + }*/ if (ab_ib_vote->lpm_mode) { for (i = 0; i < ab_ib_vote->num_src; i++) { stream_info = @@ -2830,10 +2800,6 @@ static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev_ioctl, } stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); - if (!stream_info) { - pr_err("%s: stream_info is NULL", __func__); - return -EINVAL; - } for (j = 0; j < stream_info->num_planes; j++) { for (k = 0; k < stream_info->num_isp; k++) { vfe_dev = stream_info->vfe_dev[k]; @@ -3135,18 +3101,12 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, return -EINVAL; msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); - mutex_lock(&vfe_dev_ioctl->buf_mgr->lock); + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (stream_cfg_cmd->stream_handle[i] == 0) continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); - - if (!stream_info) { - pr_err("%s: stream_info is NULL", __func__); - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); - return -EINVAL; - } if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) src_state = axi_data->src_info[ SRC_TO_INTF(stream_info->stream_src)].active; @@ -3154,7 +3114,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, else { ISP_DBG("%s: invalid src info index\n", __func__); rc = -EINVAL; - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } spin_lock_irqsave(&stream_info->lock, flags); @@ -3166,7 +3125,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, } if (rc) { spin_unlock_irqrestore(&stream_info->lock, flags); - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } @@ -3189,7 +3147,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, HANDLE_TO_IDX( stream_cfg_cmd->stream_handle[i])); spin_unlock_irqrestore(&stream_info->lock, flags); - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } for (k = 0; k < stream_info->num_isp; k++) { @@ -3248,7 +3205,6 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, spin_unlock_irqrestore(&stream_info->lock, flags); streams[num_streams++] = stream_info; } - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); for (i = 0; i < MAX_VFE; i++) { vfe_dev = update_vfes[i]; @@ -3293,10 +3249,7 @@ static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev_ioctl, continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); - if (!stream_info) { - pr_err("%s: stream_info is NULL", __func__); - return -EINVAL; - } + spin_lock_irqsave(&stream_info->lock, flags); rc = __msm_isp_check_stream_state(stream_info, 0); spin_unlock_irqrestore(&stream_info->lock, flags); @@ -3333,10 +3286,6 @@ int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) return -EINVAL; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); - if (!stream_info) { - pr_err("%s: stream_info is NULL", __func__); - return -EINVAL; - } vfe_idx = msm_isp_get_vfe_idx_for_stream_user(vfe_dev, stream_info); if (vfe_idx == -ENOTTY || stream_info->stream_handle[vfe_idx] != @@ -3779,6 +3728,13 @@ static int msm_isp_stream_axi_cfg_update(struct vfe_device *vfe_dev, unsigned long flags; int vfe_idx; + if (atomic_read(&vfe_dev->axi_data.axi_cfg_update[ + SRC_TO_INTF(stream_info->stream_src)])) { + pr_err("%s: Update in progress for vfe %d intf %d\n", + __func__, vfe_dev->pdev->id, + SRC_TO_INTF(stream_info->stream_src)); + return -EINVAL; + } spin_lock_irqsave(&stream_info->lock, flags); if (stream_info->state != ACTIVE) { spin_unlock_irqrestore(&stream_info->lock, flags); @@ -3856,11 +3812,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) } stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } if (SRC_TO_INTF(stream_info->stream_src) >= VFE_SRC_MAX) continue; if (stream_info->state != ACTIVE && @@ -3901,11 +3852,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } stream_info->buf_divert = 0; msm_isp_get_timestamp(×tamp, vfe_dev); frame_id = vfe_dev->axi_data.src_info[ @@ -3940,11 +3886,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } spin_lock_irqsave(&stream_info->lock, flags); /* no change then break early */ if (stream_info->current_framedrop_period == @@ -3978,11 +3919,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } sw_skip_info = &update_info->sw_skip_info; if (sw_skip_info->stream_src_mask != 0) { /* SW image buffer drop */ @@ -4007,11 +3943,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } rc = msm_isp_stream_axi_cfg_update(vfe_dev, stream_info, update_info); if (rc) @@ -4026,12 +3957,10 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, update_info->user_stream_id, update_info->frame_id, MSM_ISP_INVALID_BUF_INDEX); - mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); @@ -4045,11 +3974,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } rc = msm_isp_add_buf_queue(vfe_dev, stream_info, update_info->user_stream_id); if (rc) @@ -4064,11 +3988,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } msm_isp_remove_buf_queue(vfe_dev, stream_info, update_info->user_stream_id); pr_debug("%s, Remove bufq for Stream 0x%x\n", @@ -4087,12 +4006,10 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) rc = -EINVAL; break; } - mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, req_frm->user_stream_id, req_frm->frame_id, req_frm->buf_index); - mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); @@ -4105,11 +4022,6 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); - if (!stream_info) { - pr_err("%s:%d: stream_info is null", - __func__, __LINE__); - return -EINVAL; - } vfe_idx = msm_isp_get_vfe_idx_for_stream( vfe_dev, stream_info); msm_isp_stream_axi_cfg_update(vfe_dev, stream_info, @@ -4296,11 +4208,11 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts) + struct msm_isp_timestamp *ts) { int i, rc = 0; uint32_t comp_mask = 0, wm_mask = 0; - uint32_t stream_idx; + uint32_t pingpong_status, stream_idx; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_composite_info *comp_info; struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; @@ -4314,6 +4226,8 @@ void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, return; ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); + pingpong_status = + vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev); for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { rc = 0; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index 9794db5a1b9c..0f029c0d5178 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -54,7 +54,7 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type, void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts); + struct msm_isp_timestamp *ts); void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index 3e8220005f77..f0831e64f250 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -256,12 +256,13 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev, static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, uint32_t stats_irq_mask, struct msm_isp_timestamp *ts, - uint32_t pingpong_status, bool is_composite) + bool is_composite) { int i, rc = 0; struct msm_isp_event_data buf_event; struct msm_isp_stats_event *stats_event = &buf_event.u.stats; struct msm_vfe_stats_stream *stream_info = NULL; + uint32_t pingpong_status; uint32_t comp_stats_type_mask = 0; int result = 0; @@ -270,6 +271,8 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, buf_event.mono_timestamp = ts->buf_time; buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + pingpong_status = vfe_dev->hw_info-> + vfe_ops.stats_ops.get_pingpong_status(vfe_dev); for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) { if (!(stats_irq_mask & (1 << i))) @@ -306,7 +309,7 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts) + struct msm_isp_timestamp *ts) { int j, rc; uint32_t atomic_stats_mask = 0; @@ -334,7 +337,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, /* Process non-composite irq */ if (stats_irq_mask) { rc = msm_isp_stats_configure(vfe_dev, stats_irq_mask, ts, - pingpong_status, comp_flag); + comp_flag); } /* Process composite irq */ @@ -347,7 +350,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, &vfe_dev->stats_data.stats_comp_mask[j]); rc = msm_isp_stats_configure(vfe_dev, atomic_stats_mask, - ts, pingpong_status, !comp_flag); + ts, !comp_flag); } } } @@ -1102,7 +1105,6 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, struct vfe_device *vfe_dev; msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); - mutex_lock(&vfe_dev_ioctl->buf_mgr->lock); num_stats_comp_mask = vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask; @@ -1121,7 +1123,6 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, } if (rc) { spin_unlock_irqrestore(&stream_info->lock, flags); - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } rc = msm_isp_init_stats_ping_pong_reg( @@ -1129,7 +1130,6 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, if (rc < 0) { spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: No buffer for stream%d\n", __func__, idx); - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); return rc; } init_completion(&stream_info->active_comp); @@ -1164,7 +1164,6 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, stats_data->num_active_stream); streams[num_stream++] = stream_info; } - mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); for (k = 0; k < MAX_VFE; k++) { if (!update_vfes[k] || num_active_streams[k]) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h index 3efd5b57a029..2e3a24dd1f0d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, 2018 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 @@ -17,7 +17,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - uint32_t pingpong_status, struct msm_isp_timestamp *ts); + struct msm_isp_timestamp *ts); void msm_isp_stats_stream_update(struct vfe_device *vfe_dev); int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 684b331d9ac4..92b1f2ea871b 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -402,14 +402,10 @@ static int msm_isp_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, return -EINVAL; } vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, - 0, 1); msm_isp_reset_framedrop(vfe_dev, stream_info); - mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_cfg_offline_ping_pong_address(vfe_dev, stream_info, VFE_PING_FLAG, fe_cfg->output_buf_idx); - mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc < 0) { pr_err("%s: Fetch engine config failed\n", __func__); return -EINVAL; @@ -927,7 +923,6 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, case VIDIOC_MSM_ISP_AXI_RESTART: mutex_lock(&vfe_dev->core_mutex); MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev); - mutex_lock(&vfe_dev->buf_mgr->lock); if (atomic_read(&vfe_dev->error_info.overflow_state) != HALT_ENFORCED) { rc = msm_isp_stats_restart(vfe_dev); @@ -938,7 +933,6 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, pr_err_ratelimited("%s: no AXI restart, halt enforced.\n", __func__); } - mutex_unlock(&vfe_dev->buf_mgr->lock); MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev); mutex_unlock(&vfe_dev->core_mutex); break; @@ -1766,7 +1760,6 @@ int msm_isp_get_bit_per_pixel(uint32_t output_format) case V4L2_PIX_FMT_QGRBG12: case V4L2_PIX_FMT_QRGGB12: case V4L2_PIX_FMT_Y12: - case MSM_V4L2_PIX_FMT_META12: return 12; case V4L2_PIX_FMT_SBGGR14: case V4L2_PIX_FMT_SGBRG14: @@ -2068,8 +2061,7 @@ void msm_isp_prepare_tasklet_debug_info(struct vfe_device *vfe_dev, } static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, - uint32_t irq_status0, uint32_t irq_status1, - uint32_t ping_pong_status) + uint32_t irq_status0, uint32_t irq_status1) { unsigned long flags; struct msm_vfe_tasklet_queue_cmd *queue_cmd = NULL; @@ -2092,8 +2084,8 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, } queue_cmd->vfeInterruptStatus0 = irq_status0; queue_cmd->vfeInterruptStatus1 = irq_status1; - queue_cmd->vfe_pingpong_status = ping_pong_status; msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev); + queue_cmd->cmd_used = 1; queue_cmd->vfe_dev = vfe_dev; @@ -2107,7 +2099,7 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, irqreturn_t msm_isp_process_irq(int irq_num, void *data) { struct vfe_device *vfe_dev = (struct vfe_device *) data; - uint32_t irq_status0, irq_status1, ping_pong_status; + uint32_t irq_status0, irq_status1; uint32_t error_mask0, error_mask1; vfe_dev->hw_info->vfe_ops.irq_ops. @@ -2118,8 +2110,6 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) __func__, vfe_dev->pdev->id); return IRQ_HANDLED; } - ping_pong_status = vfe_dev->hw_info->vfe_ops.axi_ops. - get_pingpong_status(vfe_dev); if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) { vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq( vfe_dev, irq_status0); @@ -2147,8 +2137,7 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) return IRQ_HANDLED; } msm_isp_prepare_irq_debug_info(vfe_dev, irq_status0, irq_status1); - msm_isp_enqueue_tasklet_cmd(vfe_dev, irq_status0, irq_status1, - ping_pong_status); + msm_isp_enqueue_tasklet_cmd(vfe_dev, irq_status0, irq_status1); return IRQ_HANDLED; } @@ -2161,7 +2150,7 @@ void msm_isp_do_tasklet(unsigned long data) struct msm_vfe_irq_ops *irq_ops; struct msm_vfe_tasklet_queue_cmd *queue_cmd; struct msm_isp_timestamp ts; - uint32_t irq_status0, irq_status1, pingpong_status; + uint32_t irq_status0, irq_status1; while (1) { spin_lock_irqsave(&tasklet->tasklet_lock, flags); @@ -2177,7 +2166,6 @@ void msm_isp_do_tasklet(unsigned long data) queue_cmd->vfe_dev = NULL; irq_status0 = queue_cmd->vfeInterruptStatus0; irq_status1 = queue_cmd->vfeInterruptStatus1; - pingpong_status = queue_cmd->vfe_pingpong_status; ts = queue_cmd->ts; spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); if (vfe_dev->vfe_open_cnt == 0) { @@ -2202,11 +2190,9 @@ void msm_isp_do_tasklet(unsigned long data) } msm_isp_process_error_info(vfe_dev); irq_ops->process_stats_irq(vfe_dev, - irq_status0, irq_status1, - pingpong_status, &ts); + irq_status0, irq_status1, &ts); irq_ops->process_axi_irq(vfe_dev, - irq_status0, irq_status1, - pingpong_status, &ts); + irq_status0, irq_status1, &ts); irq_ops->process_camif_irq(vfe_dev, irq_status0, irq_status1, &ts); irq_ops->process_reg_update(vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c index 5ef08cbe9aee..06e3ee4c353b 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, 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,8 +32,6 @@ #define MSM_JPEG_NAME "jpeg" #define DEV_NAME_LEN 10 -static char devname[DEV_NAME_LEN]; - static int msm_jpeg_open(struct inode *inode, struct file *filp) { int rc = 0; @@ -187,6 +185,7 @@ static int msm_jpeg_init_dev(struct platform_device *pdev) struct msm_jpeg_device *msm_jpeg_device_p; const struct of_device_id *device_id; const struct msm_jpeg_priv_data *priv_data; + char devname[DEV_NAME_LEN]; msm_jpeg_device_p = kzalloc(sizeof(struct msm_jpeg_device), GFP_ATOMIC); if (!msm_jpeg_device_p) { @@ -329,7 +328,6 @@ static struct platform_driver msm_jpeg_driver = { static int __init msm_jpeg_driver_init(void) { int rc; - rc = platform_driver_register(&msm_jpeg_driver); return rc; } diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c index ba64433a3fab..3bd26da23176 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_dev.c @@ -400,8 +400,10 @@ static void msm_jpegdma_stop_streaming(struct vb2_queue *q) ret = -ETIME; } + mutex_lock(&ctx->jdma_device->lock); if (ctx->jdma_device->ref_count > 0) msm_jpegdma_hw_put(ctx->jdma_device); + mutex_unlock(&ctx->jdma_device->lock); } /* Videobuf2 queue callbacks. */ @@ -585,9 +587,11 @@ static int msm_jpegdma_release(struct file *file) { struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(file->private_data); + mutex_lock(&ctx->jdma_device->lock); /* release all the resources */ if (ctx->jdma_device->ref_count > 0) msm_jpegdma_hw_put(ctx->jdma_device); + mutex_unlock(&ctx->jdma_device->lock); atomic_set(&ctx->active, 0); complete_all(&ctx->completion); diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c index 994ba93907e3..19aa82a27933 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c @@ -1728,7 +1728,6 @@ error_regulators_get: */ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma) { - mutex_lock(&dma->lock); BUG_ON(dma->ref_count == 0); if (--dma->ref_count == 0) { @@ -1747,7 +1746,6 @@ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma) } /* Reset clock rate, need to be updated on next processing */ dma->active_clock_rate = -1; - mutex_unlock(&dma->lock); } /* diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 60532929a916..0f7f177a2db0 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -89,6 +89,7 @@ static uint32_t gpu_limit; __q->len--; \ list_del_init(&node->member); \ kzfree(node); \ + node = NULL; \ break; \ } \ } \ @@ -106,6 +107,7 @@ static uint32_t gpu_limit; __q->len--; \ list_del_init(&node->member); \ kzfree(node); \ + node = NULL; \ break; \ } \ } \ @@ -125,6 +127,7 @@ static uint32_t gpu_limit; if (&node->member) \ list_del_init(&node->member); \ kzfree(node); \ + node = NULL; \ } \ } \ spin_unlock_irqrestore(&__q->lock, flags); \ @@ -150,11 +153,11 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2); #define msm_queue_find(queue, type, member, func, data) ({\ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = NULL; \ + type *node = 0; \ typeof(node) __ret = NULL; \ msm_queue_find_func __f = (func); \ spin_lock_irqsave(&__q->lock, flags); \ - if (!list_empty(&__q->list)) { \ + if (!list_empty(&__q->list) && (__q->len != 0)) { \ list_for_each_entry(node, &__q->list, member) \ if ((__f) && __f(node, data)) { \ __ret = node; \ diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index bf5296e6d071..f29a3d6f30c2 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -166,33 +166,6 @@ static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev, return ret; } -static int32_t msm_buf_mngr_buf_error(struct msm_buf_mngr_device *buf_mngr_dev, - struct msm_buf_mngr_info *buf_info) -{ - unsigned long flags; - struct msm_get_bufs *bufs, *save; - int32_t ret = -EINVAL; - - spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags); - list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) { - if ((bufs->session_id == buf_info->session_id) && - (bufs->stream_id == buf_info->stream_id) && - (bufs->index == buf_info->index)) { - ret = buf_mngr_dev->vb2_ops.buf_error - (bufs->vb2_v4l2_buf, - buf_info->session_id, - buf_info->stream_id, - buf_info->frame_id, - &buf_info->timestamp, - buf_info->reserved); - list_del_init(&bufs->entry); - kfree(bufs); - break; - } - } - spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags); - return ret; -} static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev, struct msm_buf_mngr_info *buf_info) @@ -500,9 +473,6 @@ int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp) case VIDIOC_MSM_BUF_MNGR_BUF_DONE: rc = msm_buf_mngr_buf_done(msm_buf_mngr_dev, argp); break; - case VIDIOC_MSM_BUF_MNGR_BUF_ERROR: - rc = msm_buf_mngr_buf_error(msm_buf_mngr_dev, argp); - break; case VIDIOC_MSM_BUF_MNGR_PUT_BUF: rc = msm_buf_mngr_put_buf(msm_buf_mngr_dev, argp); break; @@ -599,7 +569,6 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, case VIDIOC_MSM_BUF_MNGR_GET_BUF: case VIDIOC_MSM_BUF_MNGR_BUF_DONE: case VIDIOC_MSM_BUF_MNGR_PUT_BUF: - case VIDIOC_MSM_BUF_MNGR_BUF_ERROR: rc = msm_cam_buf_mgr_ops(cmd, argp); break; case VIDIOC_MSM_BUF_MNGR_INIT: @@ -748,9 +717,6 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, case VIDIOC_MSM_BUF_MNGR_BUF_DONE32: cmd = VIDIOC_MSM_BUF_MNGR_BUF_DONE; break; - case VIDIOC_MSM_BUF_MNGR_BUF_ERROR32: - cmd = VIDIOC_MSM_BUF_MNGR_BUF_ERROR; - break; case VIDIOC_MSM_BUF_MNGR_PUT_BUF32: cmd = VIDIOC_MSM_BUF_MNGR_PUT_BUF; break; @@ -769,7 +735,6 @@ static long msm_bmgr_subdev_fops_compat_ioctl(struct file *file, switch (cmd) { case VIDIOC_MSM_BUF_MNGR_GET_BUF: case VIDIOC_MSM_BUF_MNGR_BUF_DONE: - case VIDIOC_MSM_BUF_MNGR_BUF_ERROR: case VIDIOC_MSM_BUF_MNGR_FLUSH: case VIDIOC_MSM_BUF_MNGR_PUT_BUF: { struct msm_buf_mngr_info32_t buf_info32; diff --git a/drivers/media/platform/msm/camera_v2/msm_sd.h b/drivers/media/platform/msm/camera_v2/msm_sd.h index 3d5d3e03632e..d893d9fc07e3 100644 --- a/drivers/media/platform/msm/camera_v2/msm_sd.h +++ b/drivers/media/platform/msm/camera_v2/msm_sd.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -81,9 +81,6 @@ struct msm_sd_req_vb2_q { unsigned int stream_id, uint32_t sequence, struct timeval *ts, uint32_t reserved); int (*flush_buf)(int session_id, unsigned int stream_id); - int (*buf_error)(struct vb2_v4l2_buffer *vb2_v4l2_buf, int session_id, - unsigned int stream_id, uint32_t sequence, struct timeval *ts, - uint32_t reserved); }; #define MSM_SD_NOTIFY_GET_SD 0x00000001 diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c index f2b048e37319..e271c7fcd1b6 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, 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 @@ -457,67 +457,6 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, return rc; } -static int msm_vb2_buf_error(struct vb2_v4l2_buffer *vb, int session_id, - unsigned int stream_id, uint32_t sequence, - struct timeval *ts, uint32_t buf_type) -{ - unsigned long flags, rl_flags; - struct msm_vb2_buffer *msm_vb2; - struct msm_stream *stream; - struct msm_session *session; - struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; - int rc = 0; - - session = msm_get_session(session_id); - if (IS_ERR_OR_NULL(session)) - return -EINVAL; - - read_lock_irqsave(&session->stream_rwlock, rl_flags); - - stream = msm_get_stream(session, stream_id); - if (IS_ERR_OR_NULL(stream)) { - read_unlock_irqrestore(&session->stream_rwlock, rl_flags); - return -EINVAL; - } - - spin_lock_irqsave(&stream->stream_lock, flags); - if (vb) { - list_for_each_entry(msm_vb2, &(stream->queued_list), list) { - vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); - if (vb2_v4l2_buf == vb) - break; - } - if (vb2_v4l2_buf != vb) { - pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n", - session_id, stream_id, vb); - spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock_irqrestore(&session->stream_rwlock, - rl_flags); - return -EINVAL; - } - msm_vb2 = - container_of(vb2_v4l2_buf, struct msm_vb2_buffer, - vb2_v4l2_buf); - /* put buf before buf done */ - if (msm_vb2->in_freeq) { - vb2_v4l2_buf->sequence = sequence; - vb2_v4l2_buf->timestamp = *ts; - vb2_buffer_done(&vb2_v4l2_buf->vb2_buf, - VB2_BUF_STATE_ERROR); - msm_vb2->in_freeq = 0; - rc = 0; - } else - rc = -EINVAL; - } else { - pr_err(" VB buffer is NULL for ses_id=%d, str_id=%d\n", - session_id, stream_id); - rc = -EINVAL; - } - spin_unlock_irqrestore(&stream->stream_lock, flags); - read_unlock_irqrestore(&session->stream_rwlock, rl_flags); - return rc; -} - long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, uint32_t index) { @@ -616,7 +555,6 @@ int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req) req->put_buf = msm_vb2_put_buf; req->buf_done = msm_vb2_buf_done; req->flush_buf = msm_vb2_flush_buf; - req->buf_error = msm_vb2_buf_error; return 0; } diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 4b5671cd9c28..25fc34b26bc1 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, 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 @@ -958,14 +958,9 @@ static irqreturn_t msm_cpp_irq(int irq_num, void *data) if (irq_status & 0x8) { tx_level = msm_camera_io_r(cpp_dev->base + MSM_CPP_MICRO_FIFO_TX_STAT) >> 2; - if (tx_level < MSM_CPP_TX_FIFO_LEVEL) { - for (i = 0; i < tx_level; i++) { - tx_fifo[i] = msm_camera_io_r(cpp_dev->base + - MSM_CPP_MICRO_FIFO_TX_DATA); - } - } else { - pr_err("Fatal invalid tx level %d", tx_level); - goto err; + for (i = 0; i < tx_level; i++) { + tx_fifo[i] = msm_camera_io_r(cpp_dev->base + + MSM_CPP_MICRO_FIFO_TX_DATA); } spin_lock_irqsave(&cpp_dev->tasklet_lock, flags); queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx]; @@ -1020,7 +1015,6 @@ static irqreturn_t msm_cpp_irq(int irq_num, void *data) pr_debug("DEBUG_R1: 0x%x\n", msm_camera_io_r(cpp_dev->base + 0x8C)); } -err: msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR); return IRQ_HANDLED; } @@ -1630,7 +1624,6 @@ static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, case VIDIOC_MSM_BUF_MNGR_PUT_BUF: case VIDIOC_MSM_BUF_MNGR_BUF_DONE: case VIDIOC_MSM_BUF_MNGR_GET_BUF: - case VIDIOC_MSM_BUF_MNGR_BUF_ERROR: default: { struct msm_buf_mngr_info *buff_mgr_info = (struct msm_buf_mngr_info *)arg; @@ -3624,7 +3617,7 @@ STREAM_BUFF_END: break; } buff_mgr_info.frame_id = frame_info.frame_id; - rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_ERROR, + rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_BUF_DONE, 0x0, &buff_mgr_info); if (rc < 0) { pr_err("error in buf done\n"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 6d60aabae6a5..8bc9bf00a4cd 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2018, 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 @@ -588,11 +588,6 @@ static int32_t msm_actuator_move_focus( CDBG("called, dir %d, num_steps %d\n", dir, num_steps); - if (a_ctrl->step_position_table == NULL) { - pr_err("Step Position Table is NULL\n"); - return -EINVAL; - } - if ((dest_step_pos == a_ctrl->curr_step_pos) || ((dest_step_pos <= a_ctrl->total_steps) && (a_ctrl->step_position_table[dest_step_pos] == @@ -646,8 +641,6 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); while (a_ctrl->curr_step_pos != dest_step_pos) { - if (a_ctrl->curr_region_index >= a_ctrl->region_size) - break; step_boundary = a_ctrl->region_params[a_ctrl->curr_region_index]. step_bound[dir]; @@ -1573,13 +1566,6 @@ static int msm_actuator_close(struct v4l2_subdev *sd, } kfree(a_ctrl->i2c_reg_tbl); a_ctrl->i2c_reg_tbl = NULL; - if (a_ctrl->actuator_state == ACT_OPS_ACTIVE) { - rc = msm_actuator_power_down(a_ctrl); - if (rc < 0) { - pr_err("%s:%d Actuator Power down failed\n", - __func__, __LINE__); - } - } a_ctrl->actuator_state = ACT_DISABLE_STATE; mutex_unlock(a_ctrl->actuator_mutex); CDBG("Exit\n"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index f2c765a4649f..f9d6890e26c1 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -58,6 +58,9 @@ #define PRIORITY_QUEUE (QUEUE_0) #define SYNC_QUEUE (QUEUE_1) +static struct mutex msm_cci_init_mutex = + __MUTEX_INITIALIZER(msm_cci_init_mutex); + static struct v4l2_subdev *g_cci_subdev; static void msm_cci_dump_registers(struct cci_device *cci_dev, @@ -1663,11 +1666,25 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, struct msm_camera_cci_ctrl *cci_ctrl) { int32_t rc = 0; + struct cci_device *cci_dev; + cci_dev = v4l2_get_subdevdata(sd); + if (!cci_dev || !cci_ctrl) { + pr_err("%s:%d failed: invalid params %pK %pK\n", __func__, + __LINE__, cci_dev, cci_ctrl); + rc = -EINVAL; + return rc; + } + CDBG("%s line %d cmd %d\n", __func__, __LINE__, cci_ctrl->cmd); switch (cci_ctrl->cmd) { case MSM_CCI_INIT: + + mutex_lock(cci_dev->cci_init_mutex); + rc = msm_cci_init(sd, cci_ctrl); + + mutex_unlock(cci_dev->cci_init_mutex); break; case MSM_CCI_RELEASE: rc = msm_cci_release(sd); @@ -2105,6 +2122,8 @@ static int msm_cci_probe(struct platform_device *pdev) return -EFAULT; } + new_cci_dev->cci_init_mutex = &msm_cci_init_mutex; + new_cci_dev->ref_count = 0; new_cci_dev->base = msm_camera_get_reg_base(pdev, "cci", true); if (!new_cci_dev->base) { diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h index eb615cc7a62c..eef804b096a0 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.h @@ -181,6 +181,7 @@ struct cci_device { struct workqueue_struct *write_wq[MASTER_MAX]; struct msm_camera_cci_wait_sync_cfg cci_wait_sync_cfg; uint8_t valid_sync; + struct mutex *cci_init_mutex; }; enum msm_cci_i2c_cmd_type { diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h deleted file mode 100644 index 13e560dc207d..000000000000 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/include/msm_csid_3_5_1_hwreg.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef MSM_CSID_3_5_1_HWREG_H -#define MSM_CSID_3_5_1_HWREG_H - -#include - -static uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; - -static struct csid_reg_parms_t csid_v3_5_1 = { - /* MIPI CSID registers */ - 0x0, - 0x4, - 0x8, - 0x10, - 0x14, - 0x18, - 0x1C, - 0x20, - 0x24, - 0x64, - 0x68, - 0x6C, - 0x70, - 0x74, - 0x78, - 0x7C, - 0x80, - 0x88, - 0x8C, - 0x90, - 0x94, - 0x98, - 0x9C, - 0xA0, - 0xA8, - 0xAC, - 0xB4, - 0xB8, - 0xBC, - 11, - 0x7FFF, - 0x4, - 17, - 0x30050001, - 0xC, - 0x84, - 0xA4, - 0x7f010800, - 20, - 17, - 16, -}; -#endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c index 9473fb0dc44e..7ae071176ef4 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c @@ -27,7 +27,6 @@ #include "include/msm_csid_3_4_1_hwreg.h" #include "include/msm_csid_3_4_2_hwreg.h" #include "include/msm_csid_3_6_0_hwreg.h" -#include "include/msm_csid_3_5_1_hwreg.h" #include "cam_hw_ops.h" #define V4L2_IDENT_CSID 50002 @@ -45,7 +44,6 @@ #define CSID_VERSION_V36 0x30060000 #define CSID_VERSION_V37 0x30070000 #define CSID_VERSION_V35 0x30050000 -#define CSID_VERSION_V35_1 0x30050001 #define CSID_VERSION_V40 0x40000000 #define CSID_VERSION_V50 0x50000000 #define MSM_CSID_DRV_NAME "msm_csid" @@ -1187,12 +1185,6 @@ static int csid_probe(struct platform_device *pdev) new_csid_dev->ctrl_reg->csid_lane_assign = csid_lane_assign_v3_5; new_csid_dev->hw_dts_version = CSID_VERSION_V35; - } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, - "qcom,csid-v3.5.1")) { - new_csid_dev->ctrl_reg->csid_reg = csid_v3_5_1; - new_csid_dev->ctrl_reg->csid_lane_assign = - csid_lane_assign_v3_5_1; - new_csid_dev->hw_dts_version = CSID_VERSION_V35_1; } else if (of_device_is_compatible(new_csid_dev->pdev->dev.of_node, "qcom,csid-v5.0")) { new_csid_dev->ctrl_reg->csid_reg = csid_v3_5; diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h index 4f55f4bc7c4a..9820306643f9 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, 2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #define MSM_CSIPHY_3_5_HWREG_H #define ULPM_WAKE_UP_TIMER_MODE 2 +#define GLITCH_ELIMINATION_NUM 0x12 /* bit [6:4] */ #include @@ -46,14 +47,14 @@ struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = { {0x138, 0x0}, {0x13C, 0x10}, {0x140, 0x1}, - {0x144, 0x32}, + {0x144, GLITCH_ELIMINATION_NUM}, {0x148, 0xFE}, {0x14C, 0x1}, {0x154, 0x0}, - {0x15C, 0x23}, + {0x15C, 0x33}, {0x160, ULPM_WAKE_UP_TIMER_MODE}, - {0x164, 0x50}, - {0x168, 0x70}, + {0x164, 0x48}, + {0x168, 0xA0}, {0x16C, 0x17}, {0x170, 0x41}, {0x174, 0x41}, diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index 4f7a62716810..8d091320cbca 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2018, 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 @@ -52,7 +52,6 @@ #define MAX_DPHY_DATA_LN 4 #define CLOCK_OFFSET 0x700 #define CSIPHY_SOF_DEBUG_COUNT 2 -#define GBPS 1000000000 #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) @@ -135,10 +134,8 @@ static int msm_csiphy_3phase_lane_config( uint8_t i = 0; uint16_t lane_mask = 0, lane_enable = 0, temp; void __iomem *csiphybase; - uint64_t two_gbps = 0; csiphybase = csiphy_dev->base; - two_gbps = 2 * (uint64_t)csiphy_params->lane_cnt * GBPS; lane_mask = csiphy_params->lane_mask & 0x7; while (lane_mask != 0) { temp = (i << 1)+1; @@ -284,20 +281,11 @@ static int msm_csiphy_3phase_lane_config( csiphy_3ph_reg.mipi_csiphy_3ph_lnn_ctrl51.addr + 0x200*i); } + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.data, + csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); - if ((csiphy_dev->hw_version == CSIPHY_VERSION_V35) && - (csiphy_params->data_rate > two_gbps)) { - msm_camera_io_w(0x40, - csiphybase + - csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); - } else { - msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_lnn_ctrl25.data, - csiphybase + - csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); - } lane_mask >>= 1; i++; } @@ -449,11 +437,6 @@ static int msm_csiphy_2phase_lane_config( csiphybase = csiphy_dev->base; lane_mask = csiphy_params->lane_mask & 0x1f; - - lane_enable = msm_camera_io_r(csiphybase + - csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_cmn_ctrl5.addr); - for (i = 0; i < MAX_DPHY_DATA_LN; i++) { if (mask == 0x2) { if (lane_mask & mask) @@ -491,11 +474,7 @@ static int msm_csiphy_2phase_lane_config( clk_lane = 0; } - /* In combo mode setting the 4th lane - * as clk_lane for 1 lane sensor, checking - * the lane_mask == 0x18 for one lane sensor - */ - if ((csiphy_params->combo_mode == 1) && (lane_mask == 0x18)) { + if (csiphy_params->combo_mode == 1) { val |= 0xA; if (mask == csiphy_dev->ctrl_reg-> csiphy_reg.combo_clk_mask) { @@ -541,12 +520,6 @@ static int msm_csiphy_2phase_lane_config( mipi_csiphy_2ph_lnn_cfg4.data, csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_2ph_lnn_cfg4.addr + offset); - if (lane_mask == 0x18) - msm_camera_io_w(0x80, - csiphybase + - csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_2ph_lnn_cfg1.addr + offset); - } else { msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_2ph_lnn_cfg1.data, @@ -567,8 +540,8 @@ static int msm_csiphy_2phase_lane_config( csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_2ph_lnn_cfg5.addr + offset); } - if (clk_lane == 1 && lane_mask != 0x18 && - (csiphy_dev->hw_version == CSIPHY_VERSION_V342)) { + if (clk_lane == 1 && + csiphy_dev->hw_version == CSIPHY_VERSION_V342) { msm_camera_io_w(0x1f, csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. @@ -809,10 +782,10 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, ratio = csiphy_dev->csiphy_max_clk/clk_rate; csiphy_params->settle_cnt = csiphy_params->settle_cnt/ratio; } - CDBG("%s csiphy_params, mask = 0x%x cnt = %d, data rate = %llu\n", + CDBG("%s csiphy_params, mask = 0x%x cnt = %d\n", __func__, csiphy_params->lane_mask, - csiphy_params->lane_cnt, csiphy_params->data_rate); + csiphy_params->lane_cnt); CDBG("%s csiphy_params, settle cnt = 0x%x csid %d\n", __func__, csiphy_params->settle_cnt, csiphy_params->csid_core); diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c index 457bd1730232..2c31fb22c121 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c @@ -1464,7 +1464,7 @@ int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl, switch (power_setting->seq_type) { case SENSOR_CLK: if (power_setting->seq_val >= ctrl->clk_info_size) { - pr_err("%s clk index %d >= max %zu\n", __func__, + pr_err_ratelimited("%s clk index %d >= max %zu\n", __func__, power_setting->seq_val, ctrl->clk_info_size); goto power_up_failed; @@ -1476,7 +1476,7 @@ int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl, ctrl->clk_info, ctrl->clk_ptr, ctrl->clk_info_size, true); if (rc < 0) { - pr_err("%s: clk enable failed\n", __func__); + pr_err_ratelimited("%s: clk enable failed\n", __func__); goto power_up_failed; } break; @@ -1559,7 +1559,7 @@ int msm_camera_power_up(struct msm_camera_power_ctrl_t *ctrl, CDBG("%s exit\n", __func__); return 0; power_up_failed: - pr_err("%s:%d failed\n", __func__, __LINE__); + pr_err_ratelimited("%s:%d failed\n", __func__, __LINE__); for (index--; index >= 0; index--) { CDBG("%s index %d\n", __func__, index); power_setting = &ctrl->power_setting[index]; diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index fcef05374098..2f9c6e0975e2 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -203,7 +203,9 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl) sensor_i2c_client); if (rc < 0) return rc; + rc = msm_sensor_check_id(s_ctrl); + if (rc < 0) { msm_camera_power_down(power_info, s_ctrl->sensor_device_type, sensor_i2c_client); diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 3f180735dd95..2f1c7b57b723 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -18,6 +18,22 @@ #include "msm_cci.h" #include "msm_camera_dt_util.h" +/* chenneng@camera 20151117 add for product information */ +#include +struct camera_vendor_match_tbl { + char sensor_name[32]; + char vendor_name[32]; +}; +static struct camera_vendor_match_tbl match_tbl[] = { + {"imx398","Sony"}, + {"imx362","Sony"}, + {"imx350","Sony"}, + {"imx371","Sony"}, + {"imx179","Sony"}, + {"s5k3p8sp","SAMSUNG"}, + {"imx376k","Sony"}, +}; + /* Logging macro */ #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) @@ -750,6 +766,9 @@ int32_t msm_sensor_driver_probe(void *setting, struct msm_camera_cci_client *cci_client = NULL; struct msm_camera_sensor_slave_info *slave_info = NULL; struct msm_camera_slave_info *camera_info = NULL; +/* chenneng@camera 20151117 add for product information */ + uint32_t count = 0,i; + enum COMPONENT_TYPE CameraID; unsigned long mount_pos = 0; uint32_t is_yuv; @@ -1092,6 +1111,26 @@ CSID_TG: /*Save sensor info*/ s_ctrl->sensordata->cam_slave_info = slave_info; +/* chenneng@camera 20151117 add for product information */ + if (0 == slave_info->camera_id) + CameraID = R_CAMERA; + else if (1 == slave_info->camera_id) + CameraID = SECOND_R_CAMERA; + else if (2 == slave_info->camera_id) + CameraID = F_CAMERA; + + count = ARRAY_SIZE(match_tbl); + for (i = 0;i < count;i++) { + if (!strcmp(slave_info->sensor_name,match_tbl[i].sensor_name)) + break; + } + if (i >= count) + pr_err("%s,Match camera sensor faild!,current sensor name is %s", + __func__,slave_info->sensor_name); + else + push_component_info(CameraID,slave_info->sensor_name, + match_tbl[i].vendor_name); + msm_sensor_fill_sensor_info(s_ctrl, probed_info, entity_name); /* @@ -1428,8 +1467,8 @@ static int32_t msm_sensor_driver_i2c_probe(struct i2c_client *client, rc); goto FREE_S_CTRL; } + return rc; } - return rc; FREE_S_CTRL: kfree(s_ctrl); return rc; diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c index 7e3666042fde..16de02907143 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c @@ -1,4 +1,4 @@ -/* 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 @@ -77,7 +77,7 @@ static int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init, cfg->entity_name); mutex_unlock(&s_init->imutex); if (rc < 0) - pr_err("%s failed (non-fatal) rc %d", __func__, rc); + pr_err_ratelimited("%s failed (non-fatal) rc %d", __func__, rc); break; case CFG_SINIT_PROBE_DONE: @@ -142,7 +142,7 @@ static long msm_sensor_init_subdev_do_ioctl( cmd = VIDIOC_MSM_SENSOR_INIT_CFG; rc = msm_sensor_init_subdev_ioctl(sd, cmd, &sensor_init_data); if (rc < 0) { - pr_err("%s:%d VIDIOC_MSM_SENSOR_INIT_CFG failed (non-fatal)", + pr_err_ratelimited("%s:%d VIDIOC_MSM_SENSOR_INIT_CFG failed (non-fatal)", __func__, __LINE__); return rc; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index c288568edfbd..dfffb212fd9c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -1343,7 +1343,7 @@ void *msm_vidc_open(int core_id, int session_type) goto err_invalid_core; } - pr_info(VIDC_DBG_TAG "Opening video instance: %pK, %d\n", + pr_debug(VIDC_DBG_TAG "Opening video instance: %pK, %d\n", VIDC_MSG_PRIO2STRING(VIDC_INFO), inst, session_type); mutex_init(&inst->sync_lock); mutex_init(&inst->bufq[CAPTURE_PORT].lock); @@ -1527,7 +1527,7 @@ int msm_vidc_destroy(struct msm_vidc_inst *inst) msm_vidc_debugfs_deinit_inst(inst); - pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n", + pr_debug(VIDC_DBG_TAG "Closed video instance: %pK\n", VIDC_MSG_PRIO2STRING(VIDC_INFO), inst); kfree(inst); return 0; diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c index 43c6b0651064..7ab5221cd5d7 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/drivers/mfd/wcd9xxx-irq.c @@ -536,6 +536,10 @@ int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res) wcd9xxx_res->irq_masks_cur[i]); } + /* not use elect_remove, so forbiden wcd_mbhc_hs_rem_irq */ + regmap_write(wcd9xxx_res->wcd_core_regmap, + 0x00EA, 0x2); + ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "wcd9xxx", wcd9xxx_res); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 52f75b1faec0..c9b89576cfbb 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -557,6 +557,16 @@ config UID_SYS_STATS Per UID based io statistics exported to /proc/uid_io Per UID based procstat control in /proc/uid_procstat +config BOOTLOADER_LOG + tristate "proc/bootloader_log" + depends on OF + default y + help + show bootloader log in kernel.you can + "cat proc/bootloader_log"to see current bootloader log. + To compile this driver as a module, choose M here: the module + will be called bootloader_log. + config QPNP_MISC tristate "QPNP Misc Peripheral" depends on SPMI || MSM_SPMI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b0718228d2d9..085980d7212b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o obj-y += qcom/ obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o +#add by xcb +obj-$(CONFIG_BOOTLOADER_LOG) += bootloader_log.o diff --git a/drivers/misc/bootloader_log.c b/drivers/misc/bootloader_log.c new file mode 100644 index 000000000000..1feebf457066 --- /dev/null +++ b/drivers/misc/bootloader_log.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2016 OnePlus. 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 +#include +#include + +#include +#include + +#include + +#include + + +#include +#include +#include + +#include + +#include +#include +#include +#include + +struct bootloader_log_platform_data { + unsigned long mem_size; + unsigned long mem_address; + unsigned int mem_type; + +}; + +struct bootloader_log_ram_zone_t { + phys_addr_t paddr; + size_t size; + void *vaddr; + char *buffer; + +} bootloader_log_ram_zone; + +static int bootloader_log_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", bootloader_log_ram_zone.buffer); + return 0; +} + +static int bootloader_log_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, bootloader_log_proc_show, NULL); +} + +static const struct file_operations bootloader_log_proc_fops = { + .open = bootloader_log_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int proc_bootloader_log_init(void) +{ + proc_create("bootloader_log", 0, NULL, &bootloader_log_proc_fops); + return 0; +} + + +static int __init of_bootloader_log_platform_data(struct device_node *node, + struct bootloader_log_platform_data *pdata) +{ + const u32 *addr; + u64 size; + struct device_node *pnode; + + memset(pdata, 0, sizeof(*pdata)); + + pnode = of_parse_phandle(node, "linux,contiguous-region", 0); + if (pnode) { + addr = of_get_address(pnode, 0, &size, NULL); + if (!addr) { + pr_err("failed to parse the bootloader log memory address\n"); + of_node_put(pnode); + return -EINVAL; + } + pdata->mem_address = of_read_ulong(addr, 2); + pdata->mem_size = (unsigned long) size; + of_node_put(pnode); + } else { + pr_err("mem reservation for bootloader log not present\n"); + return -EINVAL; + } + + return 0; +} + +static void *persistent_ram_vmap(phys_addr_t start, size_t size) +{ + struct page **pages; + phys_addr_t page_start; + unsigned int page_count; + pgprot_t prot; + unsigned int i; + void *vaddr; + + page_start = start - offset_in_page(start); + page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); + + prot = pgprot_noncached(PAGE_KERNEL); + + + pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); + if (!pages) + return NULL; + + for (i = 0; i < page_count; i++) { + phys_addr_t addr = page_start + i * PAGE_SIZE; + + pages[i] = pfn_to_page(addr >> PAGE_SHIFT); + } + vaddr = vmap(pages, page_count, VM_MAP, prot); + kfree(pages); + + return vaddr; +} + + +static int persistent_ram_buffer_map(phys_addr_t start, size_t size, + struct bootloader_log_ram_zone_t *blrz) +{ + blrz->paddr = start; + blrz->size = size; + + blrz->vaddr = persistent_ram_vmap(start, size); + + if (!blrz->vaddr) { + pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, + (unsigned long long)size, (unsigned long long)start); + return -ENOMEM; + } + + blrz->buffer = blrz->vaddr + offset_in_page(start); + return 0; +} + + +static int bootloader_log_probe(struct platform_device *pdev) +{ + /*struct device *dev = &pdev->dev;*/ + struct bootloader_log_platform_data *pdata = pdev->dev.platform_data; + struct bootloader_log_platform_data of_pdata; + + phys_addr_t paddr; + int err = -EINVAL; + + pr_err("XCB bootloader_log_probe\n"); + + if (pdev->dev.of_node) { + if (of_bootloader_log_platform_data(pdev->dev.of_node, + &of_pdata)) { + pr_err("Invalid bootloader log device tree data\n"); + goto fail_out; + } + pdata = &of_pdata; + } + + if (!pdata->mem_size) { + pr_err("memory size and record size must be non-zero\n"); + goto fail_out; + } + + paddr = pdata->mem_address; + + + err = persistent_ram_buffer_map(paddr, pdata->mem_size, + &bootloader_log_ram_zone); + if (err) + goto fail_out; + + proc_bootloader_log_init(); + + + pr_info("bootloader_log!\n"); + + return 0; + +fail_out: + pr_err("bootloader_log, fail_out!\n"); + return err; +} + +static int __exit bootloader_log_remove(struct platform_device *pdev) +{ + return -EBUSY; +} + +static const struct of_device_id bootloader_log_of_match[] = { + { .compatible = "bootloader_log", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bootloader_log_of_match); + +static struct platform_driver bootloader_log_driver = { + .probe = bootloader_log_probe, + .remove = __exit_p(bootloader_log_remove), + .driver = { + .name = "bootloader_log", + .owner = THIS_MODULE, + .of_match_table = bootloader_log_of_match, + }, +}; + + +static int __init bootloader_log_init(void) +{ + return platform_driver_register(&bootloader_log_driver); +} +postcore_initcall(bootloader_log_init); + +static void __exit bootloader_log_exit(void) +{ + platform_driver_unregister(&bootloader_log_driver); + +} +module_exit(bootloader_log_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("software@oneplus.cn>"); + diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index f629eef26183..16c47aaa7882 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -85,3 +85,19 @@ config NFC_NQ This enables the NFC driver for NQx based devices. This is for i2c connected version. NCI protocol logic resides in the usermode and it has no other NFC dependencies. + +config NFC_PN5XX + tristate "NXP NFC Driver" + depends on I2C + help + This enables the NFC driver for NXP NFC based devices. + +config NFC_PN80T + tristate "NFC eSE driver for NXP PN80T using NFC_PN5XX" + depends on I2C + depends on SPI + depends on NFC_PN5XX + help + This enables the SPI driver for the NXP PN80T-based embedded + secure element using the NFC_PN5XX driver to control the power. + diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 86a8fbaf0104..03b0d6051005 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -17,3 +17,6 @@ obj-$(CONFIG_NFC_ST_NCI) += st-nci/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/ obj-$(CONFIG_NFC_NQ) += nq-nci.o +obj-$(CONFIG_NFC_PN5XX) += pn5xx.o +obj-$(CONFIG_NFC_PN80T) += ese/ + diff --git a/drivers/nfc/ese/Makefile b/drivers/nfc/ese/Makefile new file mode 100755 index 000000000000..bd1615fabe2f --- /dev/null +++ b/drivers/nfc/ese/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for embedded secure element devices +# + +obj-$(CONFIG_NFC_PN80T) += p73.o + +ccflags-$(CONFIG_NFC_PN80T) := -DDEBUG + diff --git a/drivers/nfc/ese/p73.c b/drivers/nfc/ese/p73.c new file mode 100755 index 000000000000..4dddd9802362 --- /dev/null +++ b/drivers/nfc/ese/p73.c @@ -0,0 +1,725 @@ +/* + * Copyright (C) 2012-2014 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /** + * \addtogroup spi_driver + * + * @{ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "p73.h" +#include "../pn5xx.h" + +extern long pn544_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); + +/* Device driver's configuration macro */ +/* Macro to configure poll/interrupt based req*/ +//#define P61_IRQ_ENABLE + +/* Macro to configure Hard/Soft reset to P61 */ +//#define P61_HARD_RESET + +/* Macro to define SPI clock frequency */ + +//#define P61_SPI_CLOCK_7Mzh +#undef P61_SPI_CLOCK_7Mzh +#undef P61_SPI_CLOCK_10Mzh +#define P61_SPI_CLOCK_8Mzh + +#ifdef P61_SPI_CLOCK_7Mzh +#define P61_SPI_CLOCK 7000000L; +#else +#ifdef P61_SPI_CLOCK_8Mzh +#define P61_SPI_CLOCK 8000000L; +#else +#ifdef P61_SPI_CLOCK_10Mzh +#define P61_SPI_CLOCK 10000000L; +#else +#define P61_SPI_CLOCK 4000000L; +#endif +#endif +#endif + +/* size of maximum read/write buffer supported by driver */ +#define MAX_BUFFER_SIZE 258U + +/* Different driver debug lever */ +enum P61_DEBUG_LEVEL { + P61_DEBUG_OFF, + P61_FULL_DEBUG +}; + +#define READ_THROUGH_PUT 0x01 +#define WRITE_THROUGH_PUT 0x02 +#define MXAX_THROUGH_PUT_TIME 999000L +/* Variable to store current debug level request by ioctl */ +static unsigned char debug_level; + +#define P61_DBG_MSG(msg...) \ + switch(debug_level) \ + { \ + case P61_DEBUG_OFF: \ + break; \ + case P61_FULL_DEBUG: \ + printk(KERN_INFO "[NXP-P61] : " msg); \ + break; \ + default: \ + printk(KERN_ERR "[NXP-P61] : Wrong debug level %d", debug_level); \ + break; \ + } \ + +#define P61_ERR_MSG(msg...) printk(KERN_ERR "[NFC-P61] : " msg ); + +/* Device specific macro and structure */ +struct p61_dev { + wait_queue_head_t read_wq; /* wait queue for read interrupt */ + struct mutex read_mutex; /* read mutex */ + struct mutex write_mutex; /* write mutex */ + struct spi_device *spi; /* spi device structure */ + struct miscdevice p61_device; /* char device as misc driver */ + unsigned int rst_gpio; /* SW Reset gpio */ + unsigned int irq_gpio; /* P61 will interrupt DH for any ntf */ + bool irq_enabled; /* flag to indicate irq is used */ + unsigned char enable_poll_mode; /* enable the poll mode */ + spinlock_t irq_enabled_lock; /*spin lock for read irq */ +}; + +/* T==1 protocol specific global data */ +const unsigned char SOF = 0xA5u; +struct p61_through_put { + struct timeval rstart_tv; + struct timeval rstop_tv; + struct timeval wstart_tv; + struct timeval wstop_tv; + unsigned long total_through_put_wbytes; + unsigned long total_through_put_rbytes; + unsigned long total_through_put_rtime; + unsigned long total_through_put_wtime; + bool enable_through_put_measure; +}; +static struct p61_through_put p61_through_put_t; + +static void p61_start_throughput_measurement(unsigned int type); +static void p61_stop_throughput_measurement(unsigned int type, int no_of_bytes); + +static void p61_start_throughput_measurement(unsigned int type) +{ + if (type == READ_THROUGH_PUT) + { + memset(&p61_through_put_t.rstart_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&p61_through_put_t.rstart_tv); + } + else if (type == WRITE_THROUGH_PUT) + { + memset(&p61_through_put_t.wstart_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&p61_through_put_t.wstart_tv); + + } + else + { + P61_DBG_MSG(KERN_ALERT " p61_start_throughput_measurement: wrong type = %d", type); + } + +} +static void p61_stop_throughput_measurement(unsigned int type, int no_of_bytes) +{ + if (type == READ_THROUGH_PUT) + { + memset(&p61_through_put_t.rstop_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&p61_through_put_t.rstop_tv); + p61_through_put_t.total_through_put_rbytes += no_of_bytes; + p61_through_put_t.total_through_put_rtime += (p61_through_put_t.rstop_tv.tv_usec - + p61_through_put_t.rstart_tv.tv_usec) + + ((p61_through_put_t.rstop_tv.tv_sec - + p61_through_put_t.rstart_tv.tv_sec) * 1000000); + + if(p61_through_put_t.total_through_put_rtime >= MXAX_THROUGH_PUT_TIME) + { + printk(KERN_ALERT " **************** Read Throughput: **************"); + printk(KERN_ALERT " No of Read Bytes = %ld", p61_through_put_t.total_through_put_rbytes); + printk(KERN_ALERT " Total Read Time (uSec) = %ld", p61_through_put_t.total_through_put_rtime); + p61_through_put_t.total_through_put_rbytes = 0; + p61_through_put_t.total_through_put_rtime = 0; + printk(KERN_ALERT " **************** Read Throughput: **************"); + } + printk(KERN_ALERT " No of Read Bytes = %ld", p61_through_put_t.total_through_put_rbytes); + printk(KERN_ALERT " Total Read Time (uSec) = %ld", p61_through_put_t.total_through_put_rtime); + } + else if (type == WRITE_THROUGH_PUT) + { + memset(&p61_through_put_t.wstop_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&p61_through_put_t.wstop_tv); + p61_through_put_t.total_through_put_wbytes += no_of_bytes; + p61_through_put_t.total_through_put_wtime += (p61_through_put_t.wstop_tv.tv_usec - + p61_through_put_t.wstart_tv.tv_usec) + + ((p61_through_put_t.wstop_tv.tv_sec - + p61_through_put_t.wstart_tv.tv_sec) * 1000000); + + if(p61_through_put_t.total_through_put_wtime >= MXAX_THROUGH_PUT_TIME) + { + printk(KERN_ALERT " **************** Write Throughput: **************"); + printk(KERN_ALERT " No of Write Bytes = %ld", p61_through_put_t.total_through_put_wbytes); + printk(KERN_ALERT " Total Write Time (uSec) = %ld", p61_through_put_t.total_through_put_wtime); + p61_through_put_t.total_through_put_wbytes = 0; + p61_through_put_t.total_through_put_wtime = 0; + printk(KERN_ALERT " **************** WRITE Throughput: **************"); + } + printk(KERN_ALERT " No of Write Bytes = %ld", p61_through_put_t.total_through_put_wbytes); + printk(KERN_ALERT " Total Write Time (uSec) = %ld", p61_through_put_t.total_through_put_wtime); + } + else + { + printk(KERN_ALERT " p61_stop_throughput_measurement: wrong type = %d", type); + } +} +/** + * \ingroup spi_driver + * \brief Called from SPI LibEse to initilaize the P61 device + * + * \param[in] struct inode * + * \param[in] struct file * + * + * \retval 0 if ok. + * +*/ + +static int p61_dev_open(struct inode *inode, struct file *filp) { + + struct p61_dev + *p61_dev = container_of(filp->private_data, + struct p61_dev, + p61_device); + + filp->private_data = p61_dev; + P61_DBG_MSG( + "%s : Major No: %d, Minor No: %d\n", __func__, imajor(inode), iminor(inode)); + + return 0; +} + +/** + * \ingroup spi_driver + * \brief To configure the P61_SET_PWR/P61_SET_DBG/P61_SET_POLL + * \n P61_SET_PWR - hard reset (arg=2), soft reset (arg=1) + * \n P61_SET_DBG - Enable/Disable (based on arg value) the driver logs + * \n P61_SET_POLL - Configure the driver in poll (arg = 1), interrupt (arg = 0) based read operation + * \param[in] struct file * + * \param[in] unsigned int + * \param[in] unsigned long + * + * \retval 0 if ok. + * +*/ + +static long p61_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct p61_dev *p61_dev = NULL; + + unsigned char buf[100]; + + + P61_DBG_MSG(KERN_ALERT "p61_dev_ioctl-Enter %u arg = %ld\n", cmd, arg); + p61_dev = filp->private_data; + + switch (cmd) { + case P61_SET_PWR: + if (arg == 2) + { + /* Do nothing*/ + } + else if (arg == 1) + { + P61_DBG_MSG(KERN_ALERT " Soft Reset"); + //gpio_set_value(p61_dev->rst_gpio, 1); + //msleep(20); + gpio_set_value(p61_dev->rst_gpio, 0); + msleep(50); + ret = spi_read (p61_dev -> spi,(void *) buf, sizeof(buf)); + msleep(50); + gpio_set_value(p61_dev->rst_gpio, 1); + msleep(20); + + } + break; + + case P61_SET_DBG: + debug_level = (unsigned char )arg; + P61_DBG_MSG(KERN_INFO"[NXP-P61] - Debug level %d", debug_level); + break; + + case P61_SET_POLL: + + p61_dev-> enable_poll_mode = (unsigned char )arg; + if (p61_dev-> enable_poll_mode == 0) + { + P61_DBG_MSG(KERN_INFO"[NXP-P61] - IRQ Mode is set \n"); + } + else + { + P61_DBG_MSG(KERN_INFO"[NXP-P61] - Poll Mode is set \n"); + p61_dev->enable_poll_mode = 1; + } + break; + case P61_SET_SPM_PWR: + P61_DBG_MSG(KERN_ALERT " P61_SET_SPM_PWR: enter"); + ret = pn544_dev_ioctl(filp, P61_SET_SPI_PWR, arg); + P61_DBG_MSG(KERN_ALERT " P61_SET_SPM_PWR: exit"); + break; + case P61_GET_SPM_STATUS: + P61_DBG_MSG(KERN_ALERT " P61_GET_SPM_STATUS: enter"); + ret = pn544_dev_ioctl(filp, P61_GET_PWR_STATUS, arg); + P61_DBG_MSG(KERN_ALERT " P61_GET_SPM_STATUS: exit"); + break; + case P61_SET_DWNLD_STATUS: + P61_DBG_MSG(KERN_ALERT " P61_SET_DWNLD_STATUS: enter"); + ret = pn544_dev_ioctl(filp, PN544_SET_DWNLD_STATUS, arg); + P61_DBG_MSG(KERN_ALERT " P61_SET_DWNLD_STATUS: =%lu exit",arg); + break; + case P61_SET_THROUGHPUT: + p61_through_put_t.enable_through_put_measure = true; + P61_DBG_MSG(KERN_INFO"[NXP-P61] - P61_SET_THROUGHPUT enable %d", p61_through_put_t.enable_through_put_measure); + break; + case P61_GET_ESE_ACCESS: + P61_DBG_MSG(KERN_ALERT " P61_GET_ESE_ACCESS: enter"); + ret = pn544_dev_ioctl(filp, P544_GET_ESE_ACCESS, arg); + P61_DBG_MSG(KERN_ALERT " P61_GET_ESE_ACCESS ret: %d exit",ret); + break; + case P61_SET_POWER_SCHEME: + P61_DBG_MSG(KERN_ALERT " P61_SET_POWER_SCHEME: enter"); + ret = pn544_dev_ioctl(filp, P544_SET_POWER_SCHEME, arg); + P61_DBG_MSG(KERN_ALERT " P61_SET_POWER_SCHEME ret: %d exit",ret); + break; + case P61_INHIBIT_PWR_CNTRL: + P61_DBG_MSG(KERN_ALERT " P61_INHIBIT_PWR_CNTRL: enter"); + ret = pn544_dev_ioctl(filp, P544_SECURE_TIMER_SESSION, arg); + P61_DBG_MSG(KERN_ALERT " P61_INHIBIT_PWR_CNTRL ret: %d exit", ret); + break; + default: + P61_DBG_MSG(KERN_ALERT " Error case"); + ret = -EINVAL; + } + + P61_DBG_MSG(KERN_ALERT "p61_dev_ioctl-exit %u arg = %lu\n", cmd, arg); + return ret; +} + +/** + * \ingroup spi_driver + * \brief Write data to P61 on SPI + * + * \param[in] struct file * + * \param[in] const char * + * \param[in] size_t + * \param[in] loff_t * + * + * \retval data size + * +*/ + +static ssize_t p61_dev_write(struct file *filp, const char *buf, size_t count, + loff_t *offset) +{ + + int ret = -1; + struct p61_dev *p61_dev; + unsigned char tx_buffer[MAX_BUFFER_SIZE]; + + P61_DBG_MSG(KERN_ALERT "p61_dev_write -Enter count %zu\n", count); + + p61_dev = filp->private_data; + + mutex_lock(&p61_dev->write_mutex); + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + memset(&tx_buffer[0], 0, sizeof(tx_buffer)); + if (copy_from_user(&tx_buffer[0], &buf[0], count)) + { + P61_ERR_MSG("%s : failed to copy from user space\n", __func__); + mutex_unlock(&p61_dev->write_mutex); + return -EFAULT; + } + if(p61_through_put_t.enable_through_put_measure) + p61_start_throughput_measurement(WRITE_THROUGH_PUT); + /* Write data */ + ret = spi_write(p61_dev->spi, &tx_buffer[0], count); + if (ret < 0) + { + ret = -EIO; + } + else + { + ret = count; + if(p61_through_put_t.enable_through_put_measure) + p61_stop_throughput_measurement(WRITE_THROUGH_PUT, ret); + } + + mutex_unlock(&p61_dev->write_mutex); + P61_DBG_MSG(KERN_ALERT "p61_dev_write ret %d- Exit \n", ret); + return ret; +} + +/** + * \ingroup spi_driver + * \brief Used to read data from P61 in Poll/interrupt mode configured using ioctl call + * + * \param[in] struct file * + * \param[in] char * + * \param[in] size_t + * \param[in] loff_t * + * + * \retval read size + * +*/ + +static ssize_t p61_dev_read(struct file *filp, char *buf, size_t count, + loff_t *offset) +{ + int ret = -EIO; + struct p61_dev *p61_dev = filp->private_data; + unsigned char rx_buffer[MAX_BUFFER_SIZE]; + + P61_DBG_MSG("p61_dev_read count %zu - Enter \n", count); + + mutex_lock(&p61_dev->read_mutex); + if (count > MAX_BUFFER_SIZE) + { + count = MAX_BUFFER_SIZE; + } + + memset(&rx_buffer[0], 0x00, sizeof(rx_buffer)); + + if (p61_dev->enable_poll_mode) + { + P61_DBG_MSG(" %s Poll Mode Enabled \n", __FUNCTION__); + + P61_DBG_MSG(KERN_INFO"SPI_READ returned 0x%zx", count); + ret = spi_read(p61_dev->spi, (void *)&rx_buffer[0], count); + if (0 > ret) + { + P61_ERR_MSG(KERN_ALERT "spi_read failed [SOF] \n"); + goto fail; + } + } + else + { + P61_DBG_MSG(" %s P61_IRQ_ENABLE not Enabled \n", __FUNCTION__); + ret = spi_read(p61_dev->spi, (void *)&rx_buffer[0], count); + if (0 > ret) + { + P61_DBG_MSG(KERN_INFO"SPI_READ returned 0x%x", ret); + ret = -EIO; + goto fail; + } + } + + + if(p61_through_put_t.enable_through_put_measure) + p61_start_throughput_measurement(READ_THROUGH_PUT); + + if(p61_through_put_t.enable_through_put_measure) + p61_stop_throughput_measurement (READ_THROUGH_PUT, count); + P61_DBG_MSG(KERN_INFO"total_count = %zu", count); + + if (copy_to_user(buf, &rx_buffer[0], count)) + { + P61_ERR_MSG("%s : failed to copy to user space\n", __func__); + ret = -EFAULT; + goto fail; + } + P61_DBG_MSG("p61_dev_read ret %d Exit\n", ret); + P61_DBG_MSG("p61_dev_read ret %d Exit\n", rx_buffer[0]); + + mutex_unlock(&p61_dev->read_mutex); + + return ret; + + fail: + P61_ERR_MSG("Error p61_dev_read ret %d Exit\n", ret); + mutex_unlock(&p61_dev->read_mutex); + return ret; +} + +/** + * \ingroup spi_driver + * \brief It will configure the GPIOs required for soft reset, read interrupt & regulated power supply to P61. + * + * \param[in] struct p61_spi_platform_data * + * \param[in] struct p61_dev * + * \param[in] struct spi_device * + * + * \retval 0 if ok. + * +*/ + +static int p61_hw_setup(struct p61_spi_platform_data *platform_data, + struct p61_dev *p61_dev, struct spi_device *spi) +{ + int ret = -1; + + P61_DBG_MSG("Entry : %s\n", __FUNCTION__); + ret = 0; + P61_DBG_MSG("Exit : %s\n", __FUNCTION__); + return ret; +} + +/** + * \ingroup spi_driver + * \brief Set the P61 device specific context for future use. + * + * \param[in] struct spi_device * + * \param[in] void * + * + * \retval void + * +*/ + +static inline void p61_set_data(struct spi_device *spi, void *data) +{ + dev_set_drvdata(&spi->dev, data); +} + +/** + * \ingroup spi_driver + * \brief Get the P61 device specific context. + * + * \param[in] const struct spi_device * + * + * \retval Device Parameters + * +*/ + +static inline void *p61_get_data(const struct spi_device *spi) +{ + return dev_get_drvdata(&spi->dev); +} + +/* possible fops on the p61 device */ +static const struct file_operations p61_dev_fops = { + .owner = THIS_MODULE, + .read = p61_dev_read, + .write = p61_dev_write, + .open = p61_dev_open, + .unlocked_ioctl = p61_dev_ioctl, +}; +static int p61_parse_dt(struct device *dev, + struct p61_spi_platform_data *data) +{ + int errorno = 0; + + return errorno; +} + +/** + * \ingroup spi_driver + * \brief To probe for P61 SPI interface. If found initialize the SPI clock, bit rate & SPI mode. + It will create the dev entry (P61) for user space. + * + * \param[in] struct spi_device * + * + * \retval 0 if ok. + * +*/ + +static int p61_probe(struct spi_device *spi) +{ + int ret = -1; + struct p61_spi_platform_data *platform_data = NULL; + struct p61_spi_platform_data platform_data1; + struct p61_dev *p61_dev = NULL; + + P61_DBG_MSG("%s chip select : %d , bus number = %d \n", + __FUNCTION__, spi->chip_select, spi->master->bus_num); + + ret = p61_parse_dt(&spi->dev, &platform_data1); + if (ret) { + pr_err("%s - Failed to parse DT\n", __func__); + goto err_exit; + } + platform_data = &platform_data1; + p61_dev = kzalloc(sizeof(*p61_dev), GFP_KERNEL); + if (p61_dev == NULL) + { + P61_ERR_MSG("failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + ret = p61_hw_setup (platform_data, p61_dev, spi); + if (ret < 0) + { + P61_ERR_MSG("Failed to p61_enable_P61_IRQ_ENABLE\n"); + goto err_exit0; + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + spi->max_speed_hz = P61_SPI_CLOCK; + //spi->chip_select = SPI_NO_CS; + ret = spi_setup(spi); + if (ret < 0) + { + P61_ERR_MSG("failed to do spi_setup()\n"); + goto err_exit0; + } + + p61_dev -> spi = spi; + p61_dev -> p61_device.minor = MISC_DYNAMIC_MINOR; + p61_dev -> p61_device.name = "p73"; + p61_dev -> p61_device.fops = &p61_dev_fops; + p61_dev -> p61_device.parent = &spi->dev; + p61_dev->irq_gpio = platform_data->irq_gpio; + p61_dev->rst_gpio = platform_data->rst_gpio; + + + dev_set_drvdata(&spi->dev, p61_dev); + + /* init mutex and queues */ + init_waitqueue_head(&p61_dev->read_wq); + mutex_init(&p61_dev->read_mutex); + mutex_init(&p61_dev->write_mutex); + + ret = misc_register(&p61_dev->p61_device); + if (ret < 0) + { + P61_ERR_MSG("misc_register failed! %d\n", ret); + goto err_exit0; + } + + p61_dev-> enable_poll_mode = 0; /* Default IRQ read mode */ + P61_DBG_MSG("Exit : %s\n", __FUNCTION__); + return ret; + misc_deregister(&p61_dev->p61_device); + err_exit0: + mutex_destroy(&p61_dev->read_mutex); + mutex_destroy(&p61_dev->write_mutex); + if(p61_dev != NULL) + kfree(p61_dev); + err_exit: + P61_DBG_MSG("ERROR: Exit : %s ret %d\n", __FUNCTION__, ret); + return ret; +} + +/** + * \ingroup spi_driver + * \brief Will get called when the device is removed to release the resources. + * + * \param[in] struct spi_device + * + * \retval 0 if ok. + * +*/ + +static int p61_remove(struct spi_device *spi) +{ + struct p61_dev *p61_dev = p61_get_data(spi); + P61_DBG_MSG("Entry : %s\n", __FUNCTION__); + + gpio_free(p61_dev->rst_gpio); + + mutex_destroy(&p61_dev->read_mutex); + misc_deregister(&p61_dev->p61_device); + + if(p61_dev != NULL) + kfree(p61_dev); + P61_DBG_MSG("Exit : %s\n", __FUNCTION__); + return 0; +} +static struct of_device_id p61_dt_match[] = { + { + .compatible = "nxp,p61", + }, + {} +}; +static struct spi_driver p61_driver = { + .driver = { + .name = "p61", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .of_match_table = p61_dt_match, + }, + .probe = p61_probe, + .remove = (p61_remove), +}; + +/** + * \ingroup spi_driver + * \brief Module init interface + * + * \param[in] void + * + * \retval handle + * +*/ + +static int __init p61_dev_init(void) +{ + debug_level = P61_DEBUG_OFF; + + P61_DBG_MSG("Entry : %s\n", __FUNCTION__); + + return spi_register_driver(&p61_driver); + + P61_DBG_MSG("Exit : %s\n", __FUNCTION__); +} +module_init( p61_dev_init); + +/** + * \ingroup spi_driver + * \brief Module exit interface + * + * \param[in] void + * + * \retval void + * +*/ + +static void __exit p61_dev_exit(void) +{ + P61_DBG_MSG("Entry : %s\n", __FUNCTION__); + + spi_unregister_driver(&p61_driver); + P61_DBG_MSG("Exit : %s\n", __FUNCTION__); +} +module_exit( p61_dev_exit); + +MODULE_AUTHOR("BHUPENDRA PAWAR"); +MODULE_DESCRIPTION("NXP P61 SPI driver"); +MODULE_LICENSE("GPL"); + +/** @} */ diff --git a/drivers/nfc/ese/p73.h b/drivers/nfc/ese/p73.h new file mode 100755 index 000000000000..911d8e617631 --- /dev/null +++ b/drivers/nfc/ese/p73.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012-2014 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define P61_MAGIC 0xEA +#define P61_SET_PWR _IOW(P61_MAGIC, 0x01, long) +#define P61_SET_DBG _IOW(P61_MAGIC, 0x02, long) +#define P61_SET_POLL _IOW(P61_MAGIC, 0x03, long) +/* + * SPI Request NFCC to enable p61 power, only in param + * Only for SPI + * level 1 = Enable power + * level 0 = Disable power + */ +#define P61_SET_SPM_PWR _IOW(P61_MAGIC, 0x04, long) + +/* SPI or DWP can call this ioctl to get the current + * power state of P61 + * +*/ +#define P61_GET_SPM_STATUS _IOR(P61_MAGIC, 0x05, long) + +#define P61_SET_THROUGHPUT _IOW(P61_MAGIC, 0x06, long) +#define P61_GET_ESE_ACCESS _IOW(P61_MAGIC, 0x07, long) + +#define P61_SET_POWER_SCHEME _IOW(P61_MAGIC, 0x08, long) + +#define P61_SET_DWNLD_STATUS _IOW(P61_MAGIC, 0x09, long) + +#define P61_INHIBIT_PWR_CNTRL _IOW(P61_MAGIC, 0x0A, long) +struct p61_spi_platform_data { + unsigned int irq_gpio; + unsigned int rst_gpio; +}; diff --git a/drivers/nfc/pn5xx.c b/drivers/nfc/pn5xx.c new file mode 100755 index 000000000000..9d854b5e7474 --- /dev/null +++ b/drivers/nfc/pn5xx.c @@ -0,0 +1,1598 @@ +/* + * Copyright (C) 2010 Trusted Logic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +/****************************************************************************** + * + * The original Work has been changed by NXP Semiconductors. + * + * Copyright (C) 2013-2018 NXP Semiconductors + * * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* HiKey Compilation fix */ +#define HiKey_620_COMPILATION_FIX 1 +#ifndef HiKey_620_COMPILATION_FIX +#include +#endif +#include +#include +#include +#include "pn5xx.h" + +#define NEXUS5x 0 +#define HWINFO 0 +#define DRAGON_NFC 1 +#define SIG_NFC 44 +#define MAX_BUFFER_SIZE 512 +#define MAX_SECURE_SESSIONS 1 +/* Macro added to disable SVDD power toggling */ +/* #define JCOP_4X_VALIDATION */ +#define WAKEUP_SRC_TIMEOUT 5000 + +#define CLOCK_CTRL + +struct pn544_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct i2c_client *client; + struct miscdevice pn544_device; + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int irq_gpio; + unsigned int clkreq_gpio; + unsigned int ese_pwr_gpio; /* gpio used by SPI to provide power to p61 via NFCC */ + struct mutex p61_state_mutex; /* used to make p61_current_state flag secure */ + p61_access_state_t p61_current_state; /* stores the current P61 state */ + bool nfc_ven_enabled; /* stores the VEN pin state powered by Nfc */ + bool spi_ven_enabled; /* stores the VEN pin state powered by Spi */ + bool irq_enabled; + spinlock_t irq_enabled_lock; + long nfc_service_pid; /*used to signal the nfc the nfc service */ + chip_pwr_scheme_t chip_pwr_scheme; + unsigned int secure_timer_cnt; + struct workqueue_struct *pSecureTimerCbWq; + struct work_struct wq_task; + const char *clk_src_name; +#ifdef CLOCK_CTRL + struct clk *s_clk; +#endif +}; +/* HiKey Compilation fix */ +#ifndef HiKey_620_COMPILATION_FIX +struct wake_lock nfc_wake_lock; +#if HWINFO +struct hw_type_info hw_info; +#endif +static bool sIsWakeLocked = false; +#endif +static struct pn544_dev *pn544_dev; +static struct semaphore ese_access_sema; +static struct semaphore svdd_sync_onoff_sema; +static struct completion dwp_onoff_sema; +static struct timer_list secure_timer; +static void release_ese_lock(p61_access_state_t p61_current_state); +int get_ese_lock(p61_access_state_t p61_current_state, int timeout); +static long set_jcop_download_state(unsigned long arg); +static long start_seccure_timer(unsigned long timer_value); +static long secure_timer_operation(struct pn544_dev *pn544_dev, unsigned long arg); +#if HWINFO +static void check_hw_info(void); +#endif +#define SECURE_TIMER_WORK_QUEUE "SecTimerCbWq" + +static void pn544_disable_irq(struct pn544_dev *pn544_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pn544_dev->irq_enabled_lock, flags); + if (pn544_dev->irq_enabled) { + disable_irq_nosync(pn544_dev->client->irq); + //disable_irq_wake(pn544_dev->client->irq); + pn544_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&pn544_dev->irq_enabled_lock, flags); +} + +static irqreturn_t pn544_dev_irq_handler(int irq, void *dev_id) +{ + struct pn544_dev *pn544_dev = dev_id; + + if (device_may_wakeup(&pn544_dev->client->dev)) { + pm_wakeup_event(&pn544_dev->client->dev, WAKEUP_SRC_TIMEOUT); + } + + pn544_disable_irq(pn544_dev); + /* HiKey Compilation fix */ + #ifndef HiKey_620_COMPILATION_FIX + if (sIsWakeLocked == false) + { + wake_lock(&nfc_wake_lock); + sIsWakeLocked = true; + } else { + pr_debug("%s already wake locked!\n", __func__); + } + #endif + /* Wake up waiting readers */ + wake_up(&pn544_dev->read_wq); + + + return IRQ_HANDLED; +} + +static ssize_t pn544_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct pn544_dev *pn544_dev = filp->private_data; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + //pr_debug("%s : reading %zu bytes.\n", __func__, count); + + mutex_lock(&pn544_dev->read_mutex); + + if (!gpio_get_value(pn544_dev->irq_gpio)) { + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto fail; + } + + while (1) { + pn544_dev->irq_enabled = true; + enable_irq(pn544_dev->client->irq); + //enable_irq_wake(pn544_dev->client->irq); + ret = wait_event_interruptible( + pn544_dev->read_wq, + !pn544_dev->irq_enabled); + + pn544_disable_irq(pn544_dev); + + if (ret) + goto fail; + + if (gpio_get_value(pn544_dev->irq_gpio)) + break; + + pr_warning("%s: spurious interrupt detected\n", __func__); + } + } + + /* Read data */ + ret = i2c_master_recv(pn544_dev->client, tmp, count); + #ifndef HiKey_620_COMPILATION_FIX + /* HiKey Compilation fix */ + if (sIsWakeLocked == true) { + wake_unlock(&nfc_wake_lock); + sIsWakeLocked = false; + } + #endif + mutex_unlock(&pn544_dev->read_mutex); + + /* pn544 seems to be slow in handling I2C read requests + * so add 1ms delay after recv operation */ + udelay(1000); + + if (ret < 0) { + pr_err("%s: i2c_master_recv returned %d\n", __func__, ret); + return ret; + } + if (ret > count) { + pr_err("%s: received too many bytes from i2c (%d)\n", + __func__, ret); + return -EIO; + } + if (copy_to_user(buf, tmp, ret)) { + pr_warning("%s : failed to copy to user space\n", __func__); + return -EFAULT; + } + return ret; + + fail: + mutex_unlock(&pn544_dev->read_mutex); + return ret; +} + +static ssize_t pn544_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct pn544_dev *pn544_dev; + char tmp[MAX_BUFFER_SIZE]; + int ret; + + pn544_dev = filp->private_data; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + if (copy_from_user(tmp, buf, count)) { + pr_err("%s : failed to copy from user space\n", __func__); + return -EFAULT; + } + + //pr_debug("%s : writing %zu bytes.\n", __func__, count); + /* Write data */ + ret = i2c_master_send(pn544_dev->client, tmp, count); + if (ret != count) { + pr_err("%s : i2c_master_send returned %d\n", __func__, ret); + ret = -EIO; + } + + /* pn544 seems to be slow in handling I2C write requests + * so add 1ms delay after I2C send oparation */ + udelay(1000); + + return ret; +} + +static void p61_update_access_state(struct pn544_dev *pn544_dev, p61_access_state_t current_state, bool set) +{ + pr_info("%s: Enter current_state = %x\n", __func__, pn544_dev->p61_current_state); + if (current_state) + { + if(set){ + if(pn544_dev->p61_current_state == P61_STATE_IDLE) + pn544_dev->p61_current_state = P61_STATE_INVALID; + pn544_dev->p61_current_state |= current_state; + } + else{ + pn544_dev->p61_current_state ^= current_state; + if(!pn544_dev->p61_current_state) + pn544_dev->p61_current_state = P61_STATE_IDLE; + } + } + pr_info("%s: Exit current_state = %x\n", __func__, pn544_dev->p61_current_state); +} + +static void p61_get_access_state(struct pn544_dev *pn544_dev, p61_access_state_t *current_state) +{ + + if (current_state == NULL) { + //*current_state = P61_STATE_INVALID; + pr_err("%s : invalid state of p61_access_state_t current state \n", __func__); + } else { + *current_state = pn544_dev->p61_current_state; + } +} +static void p61_access_lock(struct pn544_dev *pn544_dev) +{ + pr_info("%s: Enter\n", __func__); + mutex_lock(&pn544_dev->p61_state_mutex); + pr_info("%s: Exit\n", __func__); +} +static void p61_access_unlock(struct pn544_dev *pn544_dev) +{ + pr_info("%s: Enter\n", __func__); + mutex_unlock(&pn544_dev->p61_state_mutex); + pr_info("%s: Exit\n", __func__); +} + +static int signal_handler(p61_access_state_t state, long nfc_pid) +{ + struct siginfo sinfo; + pid_t pid; + struct task_struct *task; + int sigret = 0, ret = 0; + pr_info("%s: Enter\n", __func__); + + if(nfc_pid == 0) + { + pr_info("nfc_pid is clear don't call signal_handler.\n"); + } + else + { + memset(&sinfo, 0, sizeof(struct siginfo)); + sinfo.si_signo = SIG_NFC; + sinfo.si_code = SI_QUEUE; + sinfo.si_int = state; + pid = nfc_pid; + + task = pid_task(find_vpid(pid), PIDTYPE_PID); + if(task) + { + pr_info("%s.\n", task->comm); + sigret = force_sig_info(SIG_NFC, &sinfo, task); + if(sigret < 0){ + pr_info("send_sig_info failed..... sigret %d.\n", sigret); + ret = -1; + //msleep(60); + } + } + else{ + pr_info("finding task from PID failed\r\n"); + ret = -1; + } + } + pr_info("%s: Exit ret = %d\n", __func__, ret); + return ret; +} +static void svdd_sync_onoff(long nfc_service_pid, p61_access_state_t origin) +{ + int timeout = 100; //100 ms timeout + unsigned long tempJ = msecs_to_jiffies(timeout); + pr_info("%s: Enter nfc_service_pid: %ld\n", __func__, nfc_service_pid); + if(nfc_service_pid) + { + if (0 == signal_handler(origin, nfc_service_pid)) + { + sema_init(&svdd_sync_onoff_sema, 0); + pr_info("Waiting for svdd protection response"); + if(down_timeout(&svdd_sync_onoff_sema, tempJ) != 0) + { + pr_info("svdd wait protection: Timeout"); + } + msleep(10); + pr_info("svdd wait protection : released"); + } + } + pr_info("%s: Exit\n", __func__); +} +static int release_svdd_wait(void) +{ + pr_info("%s: Enter \n", __func__); + up(&svdd_sync_onoff_sema); + pr_info("%s: Exit\n", __func__); + return 0; +} + +static void dwp_OnOff(long nfc_service_pid, p61_access_state_t origin) +{ + int timeout = 100; //100 ms timeout + unsigned long tempJ = msecs_to_jiffies(timeout); + if(nfc_service_pid) + { + if (0 == signal_handler(origin, nfc_service_pid)) + { + init_completion(&dwp_onoff_sema); + if(wait_for_completion_timeout(&dwp_onoff_sema, tempJ) != 0) + { + pr_info("Dwp On/off wait protection: Timeout"); + } + pr_info("Dwp On/Off wait protection : released"); + } + } +} +static int release_dwpOnOff_wait(void) +{ + pr_info("%s: Enter \n", __func__); + complete(&dwp_onoff_sema); + return 0; +} + +static int pn544_dev_open(struct inode *inode, struct file *filp) +{ + struct pn544_dev *pn544_dev = container_of(filp->private_data, + struct pn544_dev, + pn544_device); + + filp->private_data = pn544_dev; + + pr_debug("%s : %d,%d\n", __func__, imajor(inode), iminor(inode)); + + return 0; +} + +static int set_nfc_pid(unsigned long arg) +{ + pr_info("%s : The NFC Service PID is %ld\n", __func__, arg); + pn544_dev->nfc_service_pid = arg; + return 0; +} + +long pn544_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + pr_info("%s :enter cmd = %u, arg = %ld\n", __func__, cmd, arg); + + /* Free pass autobahn area, not protected. Use it carefullly. START */ + switch(cmd) + { + case P544_GET_ESE_ACCESS: + return get_ese_lock(P61_STATE_WIRED, arg); + break; + case P544_REL_SVDD_WAIT: + return release_svdd_wait(); + break; + case P544_SET_NFC_SERVICE_PID: + return set_nfc_pid(arg); + break; + case P544_REL_DWPONOFF_WAIT: + return release_dwpOnOff_wait(); + break; + default: + break; + } + /* Free pass autobahn area, not protected. Use it carefullly. END */ + + p61_access_lock(pn544_dev); + switch (cmd) { + case PN544_SET_PWR: + { + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + if (arg == 2) { + if (current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO) && (pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME)) + { + /* NFCC fw/download should not be allowed if p61 is used + * by SPI + */ + pr_info("%s NFCC should not be allowed to reset/FW download \n", __func__); + p61_access_unlock(pn544_dev); + return -EBUSY; /* Device or resource busy */ + } + pn544_dev->nfc_ven_enabled = true; + if ((pn544_dev->spi_ven_enabled == false && !(pn544_dev->secure_timer_cnt)) + || (pn544_dev->chip_pwr_scheme == PN80T_EXT_PMU_SCHEME)) + { + /* power on with firmware download (requires hw reset) + */ + pr_info("%s power on with firmware\n", __func__); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + if (pn544_dev->firm_gpio) { + p61_update_access_state(pn544_dev, P61_STATE_DWNLD, true); + gpio_set_value(pn544_dev->firm_gpio, 1); + } + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + } + } else if (arg == 1) { + /* power on */ + pr_info("%s power on\n", __func__); + if (pn544_dev->firm_gpio) { + if ((current_state & (P61_STATE_WIRED|P61_STATE_SPI|P61_STATE_SPI_PRIO))== 0){ + p61_update_access_state(pn544_dev, P61_STATE_IDLE, true); + } + if(current_state & P61_STATE_DWNLD){ + p61_update_access_state(pn544_dev, P61_STATE_DWNLD, false); + } + gpio_set_value(pn544_dev->firm_gpio, 0); + } + + pn544_dev->nfc_ven_enabled = true; + if (pn544_dev->spi_ven_enabled == false || (pn544_dev->chip_pwr_scheme == PN80T_EXT_PMU_SCHEME)) { + gpio_set_value(pn544_dev->ven_gpio, 1); + } + } else if (arg == 0) { + /* power off */ + pr_info("%s power off\n", __func__); + if (pn544_dev->firm_gpio) { + if ((current_state & (P61_STATE_WIRED|P61_STATE_SPI|P61_STATE_SPI_PRIO))== 0){ + p61_update_access_state(pn544_dev, P61_STATE_IDLE, true); + } + gpio_set_value(pn544_dev->firm_gpio, 0); + } + + pn544_dev->nfc_ven_enabled = false; + /* Don't change Ven state if spi made it high */ + if ((pn544_dev->spi_ven_enabled == false && !(pn544_dev->secure_timer_cnt)) + || (pn544_dev->chip_pwr_scheme == PN80T_EXT_PMU_SCHEME)) { + gpio_set_value(pn544_dev->ven_gpio, 0); + } + /* HiKey Compilation fix */ + #ifndef HiKey_620_COMPILATION_FIX + if (sIsWakeLocked == true) { + wake_unlock(&nfc_wake_lock); + sIsWakeLocked = false; + } + #endif + } else if (arg == 3) { + /*NFC Service called ISO-RST*/ + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + if(current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) { + p61_access_unlock(pn544_dev); + return -EPERM; /* Operation not permitted */ + } + if(current_state & P61_STATE_WIRED) { + p61_update_access_state(pn544_dev, P61_STATE_WIRED, false); + } + } else if (arg == 4) { + pr_info("%s FW dwldioctl called from NFC \n", __func__); + /*NFC Service called FW dwnld*/ + if (pn544_dev->firm_gpio) { + p61_update_access_state(pn544_dev, P61_STATE_DWNLD, true); + gpio_set_value(pn544_dev->firm_gpio, 1); + msleep(10); + } + } + else { + pr_err("%s bad arg %lu\n", __func__, arg); + /* changed the p61 state to idle*/ + p61_access_unlock(pn544_dev); + return -EINVAL; + } + } + break; + case P61_SET_SPI_PWR: + { + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + if (arg == 1) { + pr_info("%s : PN61_SET_SPI_PWR - power on ese\n", __func__); + if ((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0) + { + p61_update_access_state(pn544_dev, P61_STATE_SPI, true); + /*To handle triple mode protection signal + NFC service when SPI session started*/ + if (!(current_state & P61_STATE_JCP_DWNLD)){ + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + /*signal_handler(P61_STATE_SPI, pn544_dev->nfc_service_pid);*/ + dwp_OnOff(pn544_dev->nfc_service_pid, P61_STATE_SPI); + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } + pn544_dev->spi_ven_enabled = true; + + if(pn544_dev->chip_pwr_scheme == PN80T_EXT_PMU_SCHEME) + break; + + if (pn544_dev->nfc_ven_enabled == false) + { + /* provide power to NFCC if, NFC service not provided */ + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + } + /* pull the gpio to high once NFCC is power on*/ + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + + /* Delay (10ms) after SVDD_PWR_ON to allow JCOP to bootup (5ms jcop boot time + 5ms guard time) */ + usleep_range(10000, 12000); + + } else { + pr_info("%s : PN61_SET_SPI_PWR - power on ese failed \n", __func__); + p61_access_unlock(pn544_dev); + return -EBUSY; /* Device or resource busy */ + } + } else if (arg == 0) { + pr_info("%s : PN61_SET_SPI_PWR - power off ese\n", __func__); + if(current_state & P61_STATE_SPI_PRIO){ + p61_update_access_state(pn544_dev, P61_STATE_SPI_PRIO, false); + if (!(current_state & P61_STATE_JCP_DWNLD)) + { + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + if(!(current_state & P61_STATE_WIRED)) + { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START | + P61_STATE_SPI_PRIO_END); + }else { + signal_handler(P61_STATE_SPI_PRIO_END, pn544_dev->nfc_service_pid); + } + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } else if (!(current_state & P61_STATE_WIRED)) { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); + } + pn544_dev->spi_ven_enabled = false; + + if(pn544_dev->chip_pwr_scheme == PN80T_EXT_PMU_SCHEME) + break; + + /* if secure timer is running, Delay the SPI close by 25ms after sending End of Apdu to enable eSE go into DPD + gracefully (20ms after EOS + 5ms DPD settlement time) */ + if(pn544_dev->secure_timer_cnt) + usleep_range(25000, 30000); + + if (!(current_state & P61_STATE_WIRED) && !(pn544_dev->secure_timer_cnt)) + { +#ifndef JCOP_4X_VALIDATION + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + /* Delay (2.5ms) after SVDD_PWR_OFF for the shutdown settlement time */ + usleep_range(2500, 3000); +#endif + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + } +#ifndef JCOP_4X_VALIDATION + if ((pn544_dev->nfc_ven_enabled == false) && !(pn544_dev->secure_timer_cnt)) { + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + } +#endif + }else if(current_state & P61_STATE_SPI){ + p61_update_access_state(pn544_dev, P61_STATE_SPI, false); + if (!(current_state & P61_STATE_WIRED) && + (pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME) && + !(current_state & P61_STATE_JCP_DWNLD)) + { + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START | P61_STATE_SPI_END); + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + /* if secure timer is running, Delay the SPI close by 25ms after sending End of Apdu to enable eSE go into DPD + gracefully (20ms after EOS + 5ms DPD settlement time) */ + if(pn544_dev->secure_timer_cnt) + usleep_range(25000, 30000); + + if (!(pn544_dev->secure_timer_cnt)) { +#ifndef JCOP_4X_VALIDATION + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + /* Delay (2.5ms) after SVDD_PWR_OFF for the shutdown settlement time */ + usleep_range(2500, 3000); +#endif + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + } + } + /*If JCOP3.2 or 3.3 for handling triple mode + protection signal NFC service */ + else + { + if (!(current_state & P61_STATE_JCP_DWNLD)) + { + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + if(pn544_dev->chip_pwr_scheme == PN80T_LEGACY_PWR_SCHEME) + { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START | P61_STATE_SPI_END); + } else { + signal_handler(P61_STATE_SPI_END, pn544_dev->nfc_service_pid); + } + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } else if (pn544_dev->chip_pwr_scheme == PN80T_LEGACY_PWR_SCHEME) { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); + } + if(pn544_dev->chip_pwr_scheme == PN80T_LEGACY_PWR_SCHEME) + { +#ifndef JCOP_4X_VALIDATION + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); +#endif + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + pr_info("PN80T legacy ese_pwr_gpio off %s", __func__); + } + } + pn544_dev->spi_ven_enabled = false; + if (pn544_dev->nfc_ven_enabled == false && (pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME) + && !(pn544_dev->secure_timer_cnt)) { + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + } + } else { + pr_err("%s : PN61_SET_SPI_PWR - failed, current_state = %x \n", + __func__, pn544_dev->p61_current_state); + p61_access_unlock(pn544_dev); + return -EPERM; /* Operation not permitted */ + } + }else if (arg == 2) { + pr_info("%s : PN61_SET_SPI_PWR - reset\n", __func__); + if (current_state & (P61_STATE_IDLE|P61_STATE_SPI|P61_STATE_SPI_PRIO)) { + if (pn544_dev->spi_ven_enabled == false) + { + pn544_dev->spi_ven_enabled = true; + if ((pn544_dev->nfc_ven_enabled == false) && (pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME)) { + /* provide power to NFCC if, NFC service not provided */ + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + } + } + if(pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME && !(pn544_dev->secure_timer_cnt)) + { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); +#ifndef JCOP_4X_VALIDATION + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); +#endif + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + msleep(10); + if(!gpio_get_value(pn544_dev->ese_pwr_gpio)) + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + msleep(10); + } + } else { + pr_info("%s : PN61_SET_SPI_PWR - reset failed \n", __func__); + p61_access_unlock(pn544_dev); + return -EBUSY; /* Device or resource busy */ + } + }else if (arg == 3) { + pr_info("%s : PN61_SET_SPI_PWR - Prio Session Start power on ese\n", __func__); + if ((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0) { + p61_update_access_state(pn544_dev, P61_STATE_SPI_PRIO, true); + if (current_state & P61_STATE_WIRED){ + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + /*signal_handler(P61_STATE_SPI_PRIO, pn544_dev->nfc_service_pid);*/ + dwp_OnOff(pn544_dev->nfc_service_pid, P61_STATE_SPI_PRIO); + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } + pn544_dev->spi_ven_enabled = true; + if(pn544_dev->chip_pwr_scheme != PN80T_EXT_PMU_SCHEME) + { + if (pn544_dev->nfc_ven_enabled == false) { + /* provide power to NFCC if, NFC service not provided */ + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + } + /* pull the gpio to high once NFCC is power on*/ + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + + /* Delay (10ms) after SVDD_PWR_ON to allow JCOP to bootup (5ms jcop boot time + 5ms guard time) */ + usleep_range(10000, 12000); + } + }else { + pr_info("%s : Prio Session Start power on ese failed \n", __func__); + p61_access_unlock(pn544_dev); + return -EBUSY; /* Device or resource busy */ + } + }else if (arg == 4) { + if (current_state & P61_STATE_SPI_PRIO) + { + pr_info("%s : PN61_SET_SPI_PWR - Prio Session Ending...\n", __func__); + p61_update_access_state(pn544_dev, P61_STATE_SPI_PRIO, false); + /*after SPI prio timeout, the state is changing from SPI prio to SPI */ + p61_update_access_state(pn544_dev, P61_STATE_SPI, true); + if (current_state & P61_STATE_WIRED) + { + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + signal_handler(P61_STATE_SPI_PRIO_END, pn544_dev->nfc_service_pid); + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } + } + else + { + pr_info("%s : PN61_SET_SPI_PWR - Prio Session End failed \n", __func__); + p61_access_unlock(pn544_dev); + return -EBADRQC; /* Device or resource busy */ + } + } else if(arg == 5){ + release_ese_lock(P61_STATE_SPI); + } else if (arg == 6) { + /*SPI Service called ISO-RST*/ + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + if(current_state & P61_STATE_WIRED) { + p61_access_unlock(pn544_dev); + return -EPERM; /* Operation not permitted */ + } + if(current_state & P61_STATE_SPI) { + p61_update_access_state(pn544_dev, P61_STATE_SPI, false); + }else if(current_state & P61_STATE_SPI_PRIO) { + p61_update_access_state(pn544_dev, P61_STATE_SPI_PRIO, false); + } + } + else { + pr_info("%s bad ese pwr arg %lu\n", __func__, arg); + p61_access_unlock(pn544_dev); + return -EBADRQC; /* Invalid request code */ + } + } + break; + + case P61_GET_PWR_STATUS: + { + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + pr_info("%s: P61_GET_PWR_STATUS = %x",__func__, current_state); + put_user(current_state, (int __user *)arg); + } + break; + + case PN544_SET_DWNLD_STATUS: + { + long ret; + ret = set_jcop_download_state(arg); + if(ret < 0) + { + p61_access_unlock(pn544_dev); + return ret; + } + } + break; + + case P61_SET_WIRED_ACCESS: + { + p61_access_state_t current_state = P61_STATE_INVALID; + p61_get_access_state(pn544_dev, ¤t_state); + if (arg == 1) + { + if (current_state) + { + pr_info("%s : P61_SET_WIRED_ACCESS - enabling\n", __func__); + p61_update_access_state(pn544_dev, P61_STATE_WIRED, true); + if (current_state & P61_STATE_SPI_PRIO) + { + if(pn544_dev->nfc_service_pid){ + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + signal_handler(P61_STATE_SPI_PRIO, pn544_dev->nfc_service_pid); + } + else{ + pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn544_dev->nfc_service_pid); + } + } + if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0 && (pn544_dev->chip_pwr_scheme == PN67T_PWR_SCHEME)) + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + } else { + pr_info("%s : P61_SET_WIRED_ACCESS - enabling failed \n", __func__); + p61_access_unlock(pn544_dev); + return -EBUSY; /* Device or resource busy */ + } + } else if (arg == 0) { + pr_info("%s : P61_SET_WIRED_ACCESS - disabling \n", __func__); + if (current_state & P61_STATE_WIRED){ + p61_update_access_state(pn544_dev, P61_STATE_WIRED, false); + if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0 && (pn544_dev->chip_pwr_scheme == PN67T_PWR_SCHEME)) + { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + } + } else { + pr_err("%s : P61_SET_WIRED_ACCESS - failed, current_state = %x \n", + __func__, pn544_dev->p61_current_state); + p61_access_unlock(pn544_dev); + return -EPERM; /* Operation not permitted */ + } + } + else if(arg == 2) + { + pr_info("%s : P61_ESE_GPIO_LOW \n", __func__); + if(pn544_dev->chip_pwr_scheme == PN67T_PWR_SCHEME) + { + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + svdd_sync_onoff(pn544_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); + } + } + else if(arg == 3) + { + pr_info("%s : P61_ESE_GPIO_HIGH \n", __func__); + if(pn544_dev->chip_pwr_scheme == PN67T_PWR_SCHEME) + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + } + else if(arg == 4) + { + release_ese_lock(P61_STATE_WIRED); + } + else if(arg == 5) + { + gpio_set_value(pn544_dev->ese_pwr_gpio, 1); + if (gpio_get_value(pn544_dev->ese_pwr_gpio)) { + pr_info("%s: ese_pwr gpio is enabled\n", __func__); + } + } + else if(arg == 6) + { + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + pr_info("%s: ese_pwr gpio set to low\n", __func__); + } + else { + pr_info("%s P61_SET_WIRED_ACCESS - bad arg %lu\n", __func__, arg); + p61_access_unlock(pn544_dev); + return -EBADRQC; /* Invalid request code */ + } + } + break; + case P544_SET_POWER_SCHEME: + { + if(arg == PN67T_PWR_SCHEME) + { + pn544_dev->chip_pwr_scheme = PN67T_PWR_SCHEME; + pr_info("%s : The power scheme is set to PN67T legacy \n", __func__); + } + else if(arg == PN80T_LEGACY_PWR_SCHEME) + { + pn544_dev->chip_pwr_scheme = PN80T_LEGACY_PWR_SCHEME; + pr_info("%s : The power scheme is set to PN80T_LEGACY_PWR_SCHEME,\n", __func__); + } + else if(arg == PN80T_EXT_PMU_SCHEME) + { + pn544_dev->chip_pwr_scheme = PN80T_EXT_PMU_SCHEME; + pr_info("%s : The power scheme is set to PN80T_EXT_PMU_SCHEME,\n", __func__); + } + else + { + pr_info("%s : The power scheme is invalid,\n", __func__); + } + } + break; + case P544_SECURE_TIMER_SESSION: + { + secure_timer_operation(pn544_dev, arg); + } + break; + default: + pr_err("%s bad ioctl %u\n", __func__, cmd); + p61_access_unlock(pn544_dev); + return -EINVAL; + } + p61_access_unlock(pn544_dev); + pr_info("%s :exit cmd = %u, arg = %ld\n", __func__, cmd, arg); + return 0; +} +EXPORT_SYMBOL(pn544_dev_ioctl); + +static void secure_timer_workqueue(struct work_struct *Wq) +{ + p61_access_state_t current_state = P61_STATE_INVALID; + printk( KERN_INFO "secure_timer_callback: called (%lu).\n", jiffies); + /* Locking the critical section: ESE_PWR_OFF to allow eSE to shutdown peacefully :: START */ + get_ese_lock(P61_STATE_WIRED, MAX_ESE_ACCESS_TIME_OUT_MS); + p61_update_access_state(pn544_dev, P61_STATE_SECURE_MODE, false); + p61_get_access_state(pn544_dev, ¤t_state); + + if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0) + { + printk( KERN_INFO "secure_timer_callback: make se_pwer_gpio low, state = %d", current_state); + gpio_set_value(pn544_dev->ese_pwr_gpio, 0); + /* Delay (2.5ms) after SVDD_PWR_OFF for the shutdown settlement time */ + usleep_range(2500, 3000); + if(pn544_dev->nfc_service_pid == 0x00) + { + gpio_set_value(pn544_dev->ven_gpio, 0); + printk( KERN_INFO "secure_timer_callback :make ven_gpio low, state = %d", current_state); + } + } + pn544_dev->secure_timer_cnt = 0; + /* Locking the critical section: ESE_PWR_OFF to allow eSE to shutdown peacefully :: END */ + release_ese_lock(P61_STATE_WIRED); + return; +} + +static void secure_timer_callback( unsigned long data ) +{ + /* Flush and push the timer callback event to the bottom half(work queue) + to be executed later, at a safer time */ + flush_workqueue(pn544_dev->pSecureTimerCbWq); + queue_work(pn544_dev->pSecureTimerCbWq, &pn544_dev->wq_task); + return; +} + +static long start_seccure_timer(unsigned long timer_value) +{ + long ret = -EINVAL; + pr_info("start_seccure_timer: enter\n"); + /* Delete the timer if timer pending */ + if(timer_pending(&secure_timer) == 1) + { + pr_info("start_seccure_timer: delete pending timer \n"); + /* delete timer if already pending */ + del_timer(&secure_timer); + } + /* Start the timer if timer value is non-zero */ + if(timer_value) + { + init_timer(&secure_timer); + setup_timer( &secure_timer, secure_timer_callback, 0 ); + + pr_info("start_seccure_timer: timeout %lums (%lu)\n",timer_value, jiffies ); + ret = mod_timer( &secure_timer, jiffies + msecs_to_jiffies(timer_value)); + if (ret) + pr_info("start_seccure_timer: Error in mod_timer\n"); + } + return ret; +} + +static long secure_timer_operation(struct pn544_dev *pn544_dev, unsigned long arg) +{ + long ret = -EINVAL; + unsigned long timer_value = arg; + + printk( KERN_INFO "secure_timer_operation, %d\n",pn544_dev->chip_pwr_scheme); + if(pn544_dev->chip_pwr_scheme == PN80T_LEGACY_PWR_SCHEME) + { + ret = start_seccure_timer(timer_value); + if(!ret) + { + pn544_dev->secure_timer_cnt = 1; + p61_update_access_state(pn544_dev, P61_STATE_SECURE_MODE, true); + } + else + { + pn544_dev->secure_timer_cnt = 0; + p61_update_access_state(pn544_dev, P61_STATE_SECURE_MODE, false); + pr_info("%s :Secure timer reset \n", __func__); + } + } + else + { + pr_info("%s :Secure timer session not applicable \n", __func__); + } + return ret; +} + +static long set_jcop_download_state(unsigned long arg) +{ + p61_access_state_t current_state = P61_STATE_INVALID; + long ret = 0; + p61_get_access_state(pn544_dev, ¤t_state); + pr_info("%s:Enter PN544_SET_DWNLD_STATUS:JCOP Dwnld state arg = %ld",__func__, arg); + if(arg == JCP_DWNLD_INIT) + { + if(pn544_dev->nfc_service_pid) + { + pr_info("nfc service pid %s ---- %ld", __func__, pn544_dev->nfc_service_pid); + signal_handler(JCP_DWNLD_INIT, pn544_dev->nfc_service_pid); + } + else + { + if (current_state & P61_STATE_JCP_DWNLD) + { + ret = -EINVAL; + } + else + { + p61_update_access_state(pn544_dev, P61_STATE_JCP_DWNLD, true); + } + } + } + else if (arg == JCP_DWNLD_START) + { + if (current_state & P61_STATE_JCP_DWNLD) + { + ret = -EINVAL; + } + else + { + p61_update_access_state(pn544_dev, P61_STATE_JCP_DWNLD, true); + } + } + else if (arg == JCP_SPI_DWNLD_COMPLETE) + { + if(pn544_dev->nfc_service_pid) + { + signal_handler(JCP_DWP_DWNLD_COMPLETE, pn544_dev->nfc_service_pid); + } + p61_update_access_state(pn544_dev, P61_STATE_JCP_DWNLD, false); + } + else if (arg == JCP_DWP_DWNLD_COMPLETE) + { + p61_update_access_state(pn544_dev, P61_STATE_JCP_DWNLD, false); + } + else + { + pr_info("%s bad ese pwr arg %lu\n", __func__, arg); + p61_access_unlock(pn544_dev); + return -EBADRQC; /* Invalid request code */ + } + pr_info("%s: PN544_SET_DWNLD_STATUS = %x",__func__, current_state); + + return ret; +} + +int get_ese_lock(p61_access_state_t p61_current_state, int timeout) +{ + unsigned long tempJ = msecs_to_jiffies(timeout); + if(down_timeout(&ese_access_sema, tempJ) != 0) + { + printk("get_ese_lock: timeout p61_current_state = %d\n", p61_current_state); + return -EBUSY; + } + return 0; +} +EXPORT_SYMBOL(get_ese_lock); + +static void release_ese_lock(p61_access_state_t p61_current_state) +{ + up(&ese_access_sema); +} + + +static const struct file_operations pn544_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pn544_dev_read, + .write = pn544_dev_write, + .open = pn544_dev_open, + .unlocked_ioctl = pn544_dev_ioctl, +}; +static int pn544_parse_dt(struct device *dev, + struct pn544_i2c_platform_data *data) +{ + struct device_node *np = dev->of_node; + int errorno = 0; + + data->irq_gpio = of_get_named_gpio(np, "nxp,pn544-irq", 0); + if ((!gpio_is_valid(data->irq_gpio))) + return -EINVAL; + + data->ven_gpio = of_get_named_gpio(np, "nxp,pn544-ven", 0); + if ((!gpio_is_valid(data->ven_gpio))) + return -EINVAL; + + data->firm_gpio = of_get_named_gpio(np, "nxp,pn544-fw-dwnld", 0); + if ((!gpio_is_valid(data->firm_gpio))) + return -EINVAL; + + data->ese_pwr_gpio = of_get_named_gpio(np, "nxp,pn544-ese-pwr", 0); + if ((!gpio_is_valid(data->ese_pwr_gpio))) + return -EINVAL; + + data->clkreq_gpio = of_get_named_gpio(np, "nxp,pn544-clk-gpio", 0); + if ((!gpio_is_valid(data->clkreq_gpio))) + return -EINVAL; + + of_property_read_string(np, "qcom,clk-src", &data->clk_src_name); + + pr_info("%s: %d, %d, %d, %d, %d error:%d\n", __func__, + data->irq_gpio, data->ven_gpio, data->firm_gpio, + data->ese_pwr_gpio, data->clkreq_gpio, errorno); + + return errorno; +} + +static int pn544_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct pn544_i2c_platform_data *platform_data; + //struct pn544_dev *pn544_dev; + + struct device_node *node = client->dev.of_node; + + if (node) { + platform_data = devm_kzalloc(&client->dev, + sizeof(struct pn544_i2c_platform_data), GFP_KERNEL); + if (!platform_data) { + dev_err(&client->dev, + "nfc-nci probe: Failed to allocate memory\n"); + return -ENOMEM; + } + ret = pn544_parse_dt(&client->dev, platform_data); + if (ret) + { + pr_info("%s pn544_parse_dt failed", __func__); + } + client->irq = gpio_to_irq(platform_data->irq_gpio); + if (client->irq < 0) + { + pr_info("%s gpio to irq failed", __func__); + } + } else { + platform_data = client->dev.platform_data; + } + if (platform_data == NULL) { + pr_err("%s : nfc probe fail\n", __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : need I2C_FUNC_I2C\n", __func__); + return -ENODEV; + } + pn544_dev = kzalloc(sizeof(*pn544_dev), GFP_KERNEL); + if (pn544_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + + pn544_dev->irq_gpio = platform_data->irq_gpio; + pn544_dev->ven_gpio = platform_data->ven_gpio; + pn544_dev->firm_gpio = platform_data->firm_gpio; + pn544_dev->ese_pwr_gpio = platform_data->ese_pwr_gpio; + pn544_dev->clkreq_gpio = platform_data->clkreq_gpio; + pn544_dev->clk_src_name = platform_data->clk_src_name; + pn544_dev->p61_current_state = P61_STATE_IDLE; + pn544_dev->nfc_ven_enabled = false; + pn544_dev->spi_ven_enabled = false; + pn544_dev->chip_pwr_scheme = PN67T_PWR_SCHEME; + pn544_dev->client = client; + pn544_dev->secure_timer_cnt = 0; + +#ifdef CLOCK_CTRL + if (!strcmp(pn544_dev->clk_src_name, "BBCLK3")) { + pr_info("get %s\n", pn544_dev->clk_src_name); + pn544_dev->s_clk = clk_get(&pn544_dev->client->dev, "ref_clk"); + if (IS_ERR(pn544_dev->s_clk)) { + pr_err("no pclk defined\n"); + goto err_clk; + } + } + clk_set_rate(pn544_dev->s_clk, 19200000); + ret = clk_prepare_enable(pn544_dev->s_clk); + pr_info("clk_prepare_enable. ret = %d\n", ret); + if (ret) + pr_err("Can't enable s_clk\n"); +#endif + + ret = gpio_request(platform_data->irq_gpio, "nfc_int"); + if (ret < 0) { + pr_err("%s :not able to set irq_gpio as input\n", __func__); + goto err_irq; + } + ret = gpio_request(platform_data->ven_gpio, "nfc_ven"); + if (ret < 0) { + pr_err("%s : not able to set ven_gpio as output\n", __func__); + goto err_ven; + } + ret = gpio_request(platform_data->ese_pwr_gpio, "nfc_ese_pwr"); + if (ret < 0) { + pr_err("%s : not able to set ese_pwr gpio as output\n", __func__); + goto err_ese_pwr; + } + ret = gpio_request(platform_data->clkreq_gpio, "nfc_clk_gpio"); + if (ret < 0) { + pr_err("%s :not able to set clkreq_gpio as input\n", __func__); + goto err_clkreq; + } + if (platform_data->firm_gpio) { + ret = gpio_request(platform_data->firm_gpio, "nfc_firm"); + if (ret < 0) { + pr_err("%s : not able to set firm_gpio as output\n", + __func__); + goto err_firm; + } + } + + ret = gpio_direction_input(pn544_dev->irq_gpio); + if (ret < 0) { + pr_err("%s :not able to set irq_gpio as input\n", __func__); + goto err_gpio_set; + } + ret = gpio_direction_output(pn544_dev->ven_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set ven_gpio as output\n", __func__); + goto err_gpio_set; + } + ret = gpio_direction_output(pn544_dev->ese_pwr_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set ese_pwr gpio as output\n", __func__); + goto err_gpio_set; + } + ret = gpio_direction_input(pn544_dev->clkreq_gpio); + if (ret < 0) { + pr_err("%s :not able to set clkreq_gpio as input\n", __func__); + goto err_gpio_set; + } + if (platform_data->firm_gpio) { + ret = gpio_direction_output(pn544_dev->firm_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set firm_gpio as output\n", + __func__); + goto err_gpio_set; + } + } + /* init mutex and queues */ + init_waitqueue_head(&pn544_dev->read_wq); + mutex_init(&pn544_dev->read_mutex); + sema_init(&ese_access_sema, 1); + mutex_init(&pn544_dev->p61_state_mutex); + spin_lock_init(&pn544_dev->irq_enabled_lock); + pn544_dev->pSecureTimerCbWq = create_workqueue(SECURE_TIMER_WORK_QUEUE); + INIT_WORK(&pn544_dev->wq_task, secure_timer_workqueue); + pn544_dev->pn544_device.minor = MISC_DYNAMIC_MINOR; + pn544_dev->pn544_device.name = "nq-nci"; + pn544_dev->pn544_device.fops = &pn544_dev_fops; + + ret = misc_register(&pn544_dev->pn544_device); + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register; + } + /* HiKey Compilation fix */ + #ifndef HiKey_620_COMPILATION_FIX + wake_lock_init(&nfc_wake_lock, WAKE_LOCK_SUSPEND, "NFCWAKE"); + #endif + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + pr_info("%s : requesting IRQ %d\n", __func__, client->irq); + pn544_dev->irq_enabled = true; + ret = request_irq(client->irq, pn544_dev_irq_handler, + IRQF_TRIGGER_HIGH, client->name, pn544_dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_request_irq_failed; + } + enable_irq_wake(pn544_dev->client->irq); + device_init_wakeup(&client->dev, true); + device_set_wakeup_capable(&client->dev, true); + pn544_disable_irq(pn544_dev); + i2c_set_clientdata(client, pn544_dev); +#if HWINFO + /* + * This function is used only if + * hardware info is required during probe*/ + check_hw_info(); +#endif + + push_component_info(NFC, "NQ330", "NXP"); + + return 0; + + err_request_irq_failed: + misc_deregister(&pn544_dev->pn544_device); + err_misc_register: + mutex_destroy(&pn544_dev->read_mutex); + mutex_destroy(&pn544_dev->p61_state_mutex); + err_gpio_set: + if (pn544_dev->firm_gpio) + gpio_free(platform_data->firm_gpio); + err_firm: + gpio_free(platform_data->clkreq_gpio); + err_clkreq: + gpio_free(platform_data->ese_pwr_gpio); + err_ese_pwr: + gpio_free(platform_data->ven_gpio); + err_ven: + gpio_free(platform_data->irq_gpio); + err_irq: +#ifdef CLOCK_CTRL + clk_disable(pn544_dev->s_clk); + clk_put(pn544_dev->s_clk); + err_clk: +#endif + kfree(pn544_dev); + err_exit: + return ret; +} + +static int pn544_remove(struct i2c_client *client) +{ + struct pn544_dev *pn544_dev; + + pn544_dev = i2c_get_clientdata(client); +#ifdef CLOCK_CTRL + clk_disable(pn544_dev->s_clk); + clk_put(pn544_dev->s_clk); +#endif + free_irq(client->irq, pn544_dev); + misc_deregister(&pn544_dev->pn544_device); + mutex_destroy(&pn544_dev->read_mutex); + mutex_destroy(&pn544_dev->p61_state_mutex); + gpio_free(pn544_dev->irq_gpio); + gpio_free(pn544_dev->ven_gpio); + gpio_free(pn544_dev->ese_pwr_gpio); + gpio_free(pn544_dev->clkreq_gpio); + destroy_workqueue(pn544_dev->pSecureTimerCbWq); + pn544_dev->p61_current_state = P61_STATE_INVALID; + pn544_dev->nfc_ven_enabled = false; + pn544_dev->spi_ven_enabled = false; + + if (pn544_dev->firm_gpio) + gpio_free(pn544_dev->firm_gpio); + kfree(pn544_dev); + + return 0; +} + +static int pn544_suspend(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + pn544_dev = i2c_get_clientdata(client); + + if (pn544_dev->nfc_ven_enabled && gpio_get_value(pn544_dev->irq_gpio)) { + pm_wakeup_event(&pn544_dev->client->dev, WAKEUP_SRC_TIMEOUT); + return -1; + } + + return 0; +} + +static int pn544_resume(struct device *device) +{ + return 0; +} + +static const struct dev_pm_ops nfc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pn544_suspend, pn544_resume) +}; + +static const struct i2c_device_id pn544_id[] = { + { "pn544", 0 }, + { } +}; +static struct of_device_id pn544_i2c_dt_match[] = { + { + .compatible = "nxp,pn544", + }, + {} +}; +static struct i2c_driver pn544_driver = { + .id_table = pn544_id, + .probe = pn544_probe, + .remove = pn544_remove, + .driver = { + .owner = THIS_MODULE, + .name = "pn544", + .of_match_table = pn544_i2c_dt_match, + .pm = &nfc_pm_ops, + }, +}; +#if HWINFO +/****************************************************************************** + * Function check_hw_info + * + * Description This function is called during pn544_probe to retrieve + * HW info. + * Useful get HW information in case of previous FW download is + * interrupted and core reset is not allowed. + * This function checks if core reset is allowed, if not + * sets DWNLD_REQ(firm_gpio) , ven reset and sends firmware + * get version command. + * In response HW information will be received. + * + * Returns None + * + ******************************************************************************/ +static void check_hw_info() { + char read_data[20]; + int ret, get_version_len = 8, retry_count = 0; + static uint8_t cmd_reset_nci[] = {0x20, 0x00, 0x01, 0x00}; + char get_version_cmd[] = + {0x00, 0x04, 0xF1, 0x00, 0x00, 0x00, 0x6E, 0xEF}; + + pr_info("%s :Enter\n", __func__); + + /* + * Ven Reset before sending core Reset + * This is to check core reset is allowed or not. + * If not allowed then previous FW download is interrupted in between + * */ + pr_info("%s :Ven Reset \n", __func__); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + ret = i2c_master_send(pn544_dev->client, cmd_reset_nci, 4); + + if (ret == 4) { + pr_info("%s : core reset write success\n", __func__); + } else { + + /* + * Core reset failed. + * set the DWNLD_REQ , do ven reset + * send firmware download info command + * */ + pr_err("%s : write failed\n", __func__); + pr_info("%s power on with firmware\n", __func__); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + if (pn544_dev->firm_gpio) { + p61_update_access_state(pn544_dev, P61_STATE_DWNLD, true); + gpio_set_value(pn544_dev->firm_gpio, 1); + } + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + ret = i2c_master_send(pn544_dev->client, get_version_cmd, get_version_len); + if (ret != get_version_len) { + ret = -EIO; + pr_err("%s : write_failed \n", __func__); + } + else { + pr_info("%s :data sent\n", __func__); + } + + ret = 0; + + while (retry_count < 10) { + + /* + * Wait for read interrupt + * If spurious interrupt is received retry again + * */ + pn544_dev->irq_enabled = true; + enable_irq(pn544_dev->client->irq); + //enable_irq_wake(pn544_dev->client->irq); + ret = wait_event_interruptible( + pn544_dev->read_wq, + !pn544_dev->irq_enabled); + + pn544_disable_irq(pn544_dev); + + if (gpio_get_value(pn544_dev->irq_gpio)) + break; + + pr_warning("%s: spurious interrupt detected\n", __func__); + retry_count ++; + } + + if(ret) { + return; + } + + /* + * Read response data and copy into hw_type_info + * */ + ret = i2c_master_recv(pn544_dev->client, read_data, 14); + + if(ret) { + memcpy(hw_info.data, read_data, ret); + hw_info.len = ret; + pr_info("%s :data received len : %d\n", __func__,hw_info.len); + } + else { + pr_err("%s :Read Failed\n", __func__); + } + } +} +#endif +/* + * module load/unload record keeping + */ + +static int __init pn544_dev_init(void) +{ + pr_info("Loading pn544 driver\n"); + return i2c_add_driver(&pn544_driver); +} +module_init(pn544_dev_init); + +static void __exit pn544_dev_exit(void) +{ + pr_info("Unloading pn544 driver\n"); + i2c_del_driver(&pn544_driver); +} +module_exit(pn544_dev_exit); + +MODULE_AUTHOR("Sylvain Fonteneau"); +MODULE_DESCRIPTION("NFC PN544 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/pn5xx.h b/drivers/nfc/pn5xx.h new file mode 100755 index 000000000000..e6b93746f2b0 --- /dev/null +++ b/drivers/nfc/pn5xx.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2010 Trusted Logic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/****************************************************************************** + * + * The original Work has been changed by NXP Semiconductors. + * + * Copyright (C) 2013-2014 NXP Semiconductors + * * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ +#ifndef _PN553_H_ +#define _PN553_H_ +#define PN544_MAGIC 0xE9 + +/* + * PN544 power control via ioctl + * PN544_SET_PWR(0): power off + * PN544_SET_PWR(1): power on + * PN544_SET_PWR(2): reset and power on with firmware download enabled + */ +#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, int) + +/* + * SPI Request NFCC to enable p61 power, only in param + * Only for SPI + * level 1 = Enable power + * level 0 = Disable power + */ +#define P61_SET_SPI_PWR _IOW(PN544_MAGIC, 0x02, int) + +/* SPI or DWP can call this ioctl to get the current + * power state of P61 + * +*/ +#define P61_GET_PWR_STATUS _IOR(PN544_MAGIC, 0x03, int) + +/* DWP side this ioctl will be called + * level 1 = Wired access is enabled/ongoing + * level 0 = Wired access is disalbed/stopped +*/ +#define P61_SET_WIRED_ACCESS _IOW(PN544_MAGIC, 0x04, int) + +/* + NFC Init will call the ioctl to register the PID with the i2c driver +*/ +#define P544_SET_NFC_SERVICE_PID _IOW(PN544_MAGIC, 0x05, int) + +/* + NFC and SPI will call the ioctl to get the i2c/spi bus access +*/ +#define P544_GET_ESE_ACCESS _IOW(PN544_MAGIC, 0x06, int) +/* + NFC and SPI will call the ioctl to update the power scheme +*/ +#define P544_SET_POWER_SCHEME _IOW(PN544_MAGIC, 0x07, int) + +/* + NFC will call the ioctl to release the svdd protection +*/ +#define P544_REL_SVDD_WAIT _IOW(PN544_MAGIC, 0x08, int) + +/* SPI or DWP can call this ioctl to get the current + * power state of P61 + * +*/ +#define PN544_SET_DWNLD_STATUS _IOW(PN544_MAGIC, 0x09, int) +/* + NFC will call the ioctl to release the dwp on/off protection +*/ +#define P544_REL_DWPONOFF_WAIT _IOW(PN544_MAGIC, 0x0A, int) + +/* + NFC will call the ioctl to start Secure Timer +*/ + +#define P544_SECURE_TIMER_SESSION _IOW(PN544_MAGIC, 0x0B, int) + +#define MAX_ESE_ACCESS_TIME_OUT_MS 200 /*100 milliseconds*/ + +typedef enum p61_access_state{ + P61_STATE_INVALID = 0x0000, + P61_STATE_IDLE = 0x0100, /* p61 is free to use */ + P61_STATE_WIRED = 0x0200, /* p61 is being accessed by DWP (NFCC)*/ + P61_STATE_SPI = 0x0400, /* P61 is being accessed by SPI */ + P61_STATE_DWNLD = 0x0800, /* NFCC fw download is in progress */ + P61_STATE_SPI_PRIO = 0x1000, /*Start of p61 access by SPI on priority*/ + P61_STATE_SPI_PRIO_END = 0x2000, /*End of p61 access by SPI on priority*/ + P61_STATE_SPI_END = 0x4000, + P61_STATE_JCP_DWNLD = 0x8000,/* JCOP downlad in progress */ + P61_STATE_SECURE_MODE = 0x100000, /* secure mode state*/ + P61_STATE_SPI_SVDD_SYNC_START = 0x0001, /*ESE_VDD Low req by SPI*/ + P61_STATE_SPI_SVDD_SYNC_END = 0x0002, /*ESE_VDD is Low by SPI*/ + P61_STATE_DWP_SVDD_SYNC_START = 0x0004, /*ESE_VDD Low req by Nfc*/ + P61_STATE_DWP_SVDD_SYNC_END = 0x0008 /*ESE_VDD is Low by Nfc*/ +}p61_access_state_t; + +typedef enum chip_type_pwr_scheme{ + PN67T_PWR_SCHEME = 0x01, + PN80T_LEGACY_PWR_SCHEME, + PN80T_EXT_PMU_SCHEME, +}chip_pwr_scheme_t; + +typedef enum jcop_dwnld_state{ + JCP_DWNLD_IDLE = P61_STATE_JCP_DWNLD, /* jcop dwnld is ongoing*/ + JCP_DWNLD_INIT=0x8010, /* jcop dwonload init state*/ + JCP_DWNLD_START=0x8020, /* download started */ + JCP_SPI_DWNLD_COMPLETE=0x8040, /* jcop download complete in spi interface*/ + JCP_DWP_DWNLD_COMPLETE=0x8080, /* jcop download complete */ +} jcop_dwnld_state_t; + +struct pn544_i2c_platform_data { + unsigned int irq_gpio; + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int clkreq_gpio; + unsigned int ese_pwr_gpio; /* gpio to give power to p61, only TEE should use this */ + const char *clk_src_name; +}; + +struct hw_type_info { + /* + * Response of get_version_cmd will be stored in data + * byte structure : + * byte 0-1 : Header + * byte 2 : Status + * byte 3 : Hardware Version + * byte 4 : ROM code + * byte 5 : 0x00 constant + * byte 6-7 : Protected data version + * byte 8-9 : Trim data version + * byte 10-11 : FW version + * byte 12-13 : CRC + * */ + char data[20]; + int len; +}; +#endif diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index ca175710c4c8..da7cc17626ed 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -29,6 +29,13 @@ #include /* for COMMAND_LINE_SIZE */ #include +#include +static unsigned long long ddr_size = 0; +module_param(ddr_size, ullong, S_IRUGO); +MODULE_PARM_DESC(ddr_size, "ddr size"); + +void init_param_mem_base_size(phys_addr_t base, unsigned long size); + /* * of_fdt_limit_memory - limit the number of regions in the /memory node * @limit: maximum entries @@ -502,6 +509,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", uname, &base, (unsigned long)size / SZ_1M); + if(!strncmp(uname, "param_mem",9)){ + init_param_mem_base_size(base,size); + } + len -= t_len; if (first) { fdt_reserved_mem_save_node(node, uname, base, size); @@ -942,7 +953,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, continue; pr_debug(" - %llx , %llx\n", (unsigned long long)base, (unsigned long long)size); - + ddr_size += size; early_init_dt_add_memory_arch(base, size); } diff --git a/drivers/oneplus/Kconfig b/drivers/oneplus/Kconfig new file mode 100644 index 000000000000..01e05385bde7 --- /dev/null +++ b/drivers/oneplus/Kconfig @@ -0,0 +1,8 @@ +config OEM_DEBUG_SUPPORT + bool + default y + +config OEM_TRACE_SUPPORT + default y + depends on OEM_DEBUG_SUPPORT + bool "OEM debug function, enable it will register the device, which under /dev/otracer." diff --git a/drivers/oneplus/Makefile b/drivers/oneplus/Makefile new file mode 100644 index 000000000000..dac5df466177 --- /dev/null +++ b/drivers/oneplus/Makefile @@ -0,0 +1,5 @@ +ifdef CONFIG_OEM_DEBUG_SUPPORT +# oem trace +obj-$(CONFIG_OEM_TRACE_SUPPORT) += oem_trace.o +obj-y += boot_mode.o +endif diff --git a/drivers/oneplus/boot_mode.c b/drivers/oneplus/boot_mode.c new file mode 100644 index 000000000000..6947d248da99 --- /dev/null +++ b/drivers/oneplus/boot_mode.c @@ -0,0 +1,74 @@ +#include +#include +#include + +static enum oem_boot_mode boot_mode = MSM_BOOT_MODE__NORMAL; + +char *enum_ftm_mode[] = {"normal", + "fastboot", + "recovery", + "aging", + "ftm_at", + "ftm_rf", + "ftm_wlan", + "ftm_mos", + "charge" +}; + +enum oem_boot_mode get_boot_mode(void) +{ + return boot_mode; +} +EXPORT_SYMBOL(get_boot_mode); + +static int __init boot_mode_init(char *str) +{ + + pr_info("boot_mode_init %s\n", str); + + if (str) { + if (strncmp(str, "ftm_at", 6) == 0) + boot_mode = MSM_BOOT_MODE__FACTORY; + else if (strncmp(str, "ftm_rf", 6) == 0) + boot_mode = MSM_BOOT_MODE__RF; + else if (strncmp(str, "ftm_wlan", 8) == 0) + boot_mode = MSM_BOOT_MODE__WLAN; + else if (strncmp(str, "ftm_mos", 7) == 0) + boot_mode = MSM_BOOT_MODE__MOS; + else if (strncmp(str, "ftm_recovery", 12) == 0) + boot_mode = MSM_BOOT_MODE__RECOVERY; + else if (strncmp(str, "ftm_aging", 9) == 0) + boot_mode = MSM_BOOT_MODE__AGING; + } + + pr_info("kernel boot_mode = %s[%d]\n", + enum_ftm_mode[boot_mode], boot_mode); + return 0; +} +__setup("androidboot.ftm_mode=", boot_mode_init); + + +static int __init boot_mode_init_normal(void) +{ + char *substrftm = strstr(boot_command_line, "androidboot.ftm_mode="); + char *substrnormal = strstr(boot_command_line, "androidboot.mode="); + char *substrftmstr = NULL ; + char *substrnormalstr = NULL ; + + substrftmstr = substrftm + strlen("androidboot.ftm_mode="); + substrnormalstr = substrnormal + strlen("androidboot.mode="); + + if(substrftm != NULL && substrftmstr != NULL) { + + }else if(substrnormal != NULL && substrnormalstr != NULL) { + + if(strncmp(substrnormalstr, "recovery", 8) == 0) + boot_mode = MSM_BOOT_MODE__RECOVERY; + else if(strncmp(substrnormalstr, "charger", 7) == 0) + boot_mode = MSM_BOOT_MODE__CHARGE; + } + + pr_info("kernel normal boot_mode = %s[%d]\n",enum_ftm_mode[boot_mode],boot_mode); + return 0; +} +arch_initcall(boot_mode_init_normal); \ No newline at end of file diff --git a/drivers/oneplus/oem_trace.c b/drivers/oneplus/oem_trace.c new file mode 100644 index 000000000000..4e652e07ec58 --- /dev/null +++ b/drivers/oneplus/oem_trace.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2010 op, Inc. + * Author: Andy + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_CMA +#include +#endif + +#include "oem_trace.h" + + +#define IOCTL_OTRACER_TEST (1<<0) +#define IOCTL_OTRACER_STACK (1<<1) +#define IOCTL_OTRACER_MEMINFO (1<<2) +#define IOCTL_OTRACER_TASKINFO (1<<3) +#define IOCTL_OTRACER_ALLINFO (1<<4) +#define IOCTL_OTRACER_TOLCD (1<<5) + +#define IOCTL_OTRACER_PANIC (1<<12) + +struct vmalloc_info { + unsigned long used; + unsigned long largest_chunk; +}; + + +void backtrace_test_saved(void) +{ + struct stack_trace trace; + unsigned long entries[8]; + + pr_info("\nThe following trace is a kernel self test and not a bug!\n"); + + trace.nr_entries = 0; + trace.max_entries = ARRAY_SIZE(entries); + trace.entries = entries; + trace.skip = 0; + + pr_info("Testing a dump_stack.\n"); + dump_stack(); + pr_info("Testing a print_stack_trace.\n"); + print_stack_trace(&trace, 0); +} + +void tasks_mem_get( +struct mm_struct *mm, unsigned long *vsize, unsigned long *vrss) +{ + unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss; + + hiwater_vm = total_vm = mm->total_vm; + if (hiwater_vm < mm->hiwater_vm) + hiwater_vm = mm->hiwater_vm; + hiwater_rss = total_rss = get_mm_rss(mm); + if (hiwater_rss < mm->hiwater_rss) + hiwater_rss = mm->hiwater_rss; + + *vsize = total_vm << (PAGE_SHIFT-10); + *vrss = total_rss << (PAGE_SHIFT-10); +} + +static char *task_state_array[] = { + "R-0", /* 0 (running) */ + "S-1", /* 1 (sleeping) */ + "D-2", /* 2 (disk sleep) */ + "T-4", /* 4 (stopped) */ + "T-8", /* 8 (tracing stop) */ + "Z-F", /* 16 (zombie) */ + "X-" /* 32 (dead) */ +}; + +static inline const char *get_task_state(struct task_struct *tsk) +{ + unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state; + char **p = &task_state_array[0]; + + while (state) { + p++; + state >>= 1; + } + return *p; +} + +void tasks_test_saved(void) +{ + struct task_struct *p; + struct cred *cred = NULL; + struct mm_struct *mm; + unsigned long vsize = 0, vrss = 0; + + pr_info("\nThe following trace is a kernel tasks test and not a bug!\n"); + + pr_info("USER\tPID\tVSIZE\tRSS\tSTATE\tNAME\n"); + write_lock_irq(&tasklist_lock); + for_each_process(p) { + + cred = (struct cred *)get_cred((struct cred *) __task_cred(p)); + + vsize = 0; + vrss = 0; + mm = get_task_mm(p); + + if (mm) + tasks_mem_get(mm, &vsize, &vrss); + + pr_info("%u\t%d\t%ld\t%ld\t%s\t%s\n", + (cred->uid).val, + task_pid_nr(p), + vsize, + vrss, + get_task_state(p), + p->comm); + } + write_unlock_irq(&tasklist_lock); +} + +void meminfo_test_saved(void) +{ +struct sysinfo i; + unsigned long committed; + unsigned long allowed; + long cached; + unsigned long pages[NR_LRU_LISTS]; + int lru; + +/* + * display in kilobytes. + */ +#define K(x) ((x) << (PAGE_SHIFT - 10)) + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); + allowed = ((totalram_pages - hugetlb_total_pages()) + * sysctl_overcommit_ratio / 100) + total_swap_pages; + + cached = global_page_state(NR_FILE_PAGES) - + total_swapcache_pages() - i.bufferram; + if (cached < 0) + cached = 0; + + + for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) + pages[lru] = global_page_state(NR_LRU_BASE + lru); + + /* + * Tagged format, for easy grepping and expansion. + */ + pr_info("Meminfo:\n" + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" + "Buffers: %8lu kB\n" + "Cached: %8lu kB\n" + "SwapCached: %8lu kB\n" + "Active: %8lu kB\n" + "Inactive: %8lu kB\n" + "Active(anon): %8lu kB\n" + "Inactive(anon): %8lu kB\n" + "Active(file): %8lu kB\n" + "Inactive(file): %8lu kB\n" + "Unevictable: %8lu kB\n" + "Mlocked: %8lu kB\n" +#ifdef CONFIG_HIGHMEM + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" + "LowTotal: %8lu kB\n" + "LowFree: %8lu kB\n" +#endif +#ifndef CONFIG_MMU + "MmapCopy: %8lu kB\n" +#endif + "SwapTotal: %8lu kB\n" + "SwapFree: %8lu kB\n" + "Dirty: %8lu kB\n" + "Writeback: %8lu kB\n" + "AnonPages: %8lu kB\n" + "Mapped: %8lu kB\n" + "Shmem: %8lu kB\n" + "Slab: %8lu kB\n" + "SReclaimable: %8lu kB\n" + "SUnreclaim: %8lu kB\n" + "KernelStack: %8lu kB\n" + "PageTables: %8lu kB\n" +#ifdef CONFIG_QUICKLIST + "Quicklists: %8lu kB\n" +#endif + "NFS_Unstable: %8lu kB\n" + "Bounce: %8lu kB\n" + "WritebackTmp: %8lu kB\n" + "CommitLimit: %8lu kB\n" + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" + "VmallocChunk: %8lu kB\n" +#ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" +#endif +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + "AnonHugePages: %8lu kB\n" +#endif +#ifdef CONFIG_CMA + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" +#endif + , + K(i.totalram), + K(i.freeram), + K(i.bufferram), + K(cached), + K(total_swapcache_pages()), + K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]), + K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]), + K(pages[LRU_ACTIVE_ANON]), + K(pages[LRU_INACTIVE_ANON]), + K(pages[LRU_ACTIVE_FILE]), + K(pages[LRU_INACTIVE_FILE]), + K(pages[LRU_UNEVICTABLE]), + K(global_page_state(NR_MLOCK)), +#ifdef CONFIG_HIGHMEM + K(i.totalhigh), + K(i.freehigh), + K(i.totalram-i.totalhigh), + K(i.freeram-i.freehigh), +#endif +#ifndef CONFIG_MMU + K((unsigned long) atomic_long_read(&mmap_pages_allocated)), +#endif + K(i.totalswap), + K(i.freeswap), + K(global_page_state(NR_FILE_DIRTY)), + K(global_page_state(NR_WRITEBACK)), +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + K(global_page_state(NR_ANON_PAGES) + + global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR), +#else + K(global_page_state(NR_ANON_PAGES)), +#endif + K(global_page_state(NR_FILE_MAPPED)), + K(global_page_state(NR_SHMEM)), + K(global_page_state(NR_SLAB_RECLAIMABLE) + + global_page_state(NR_SLAB_UNRECLAIMABLE)), + K(global_page_state(NR_SLAB_RECLAIMABLE)), + K(global_page_state(NR_SLAB_UNRECLAIMABLE)), + global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024, + K(global_page_state(NR_PAGETABLE)), +#ifdef CONFIG_QUICKLIST + K(quicklist_total_size()), +#endif + K(global_page_state(NR_UNSTABLE_NFS)), + K(global_page_state(NR_BOUNCE)), + K(global_page_state(NR_WRITEBACK_TEMP)), + K(allowed), + K(committed), + (unsigned long)VMALLOC_TOTAL >> 10, + 0ul, /* used to be vmalloc 'used'*/ + 0ul /* used to be vmalloc 'largest_chunk'*/ +#ifdef CONFIG_MEMORY_FAILURE + , atomic_long_read(&num_poisoned_pages) << (PAGE_SHIFT - 10) +#endif +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + , K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR) +#endif +#ifdef CONFIG_CMA + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) +#endif + ); +#undef K +} + +int oem_con_write(const unsigned char *buf, int count) +{ + return 0; +} + +void console_activate(void) +{ +} + + +static ssize_t otracer_read(struct file *filp, char __user *buf, + size_t size, loff_t *offp) +{ + pr_info("otracer_read: initialized\n"); + return 0; +} + + +static int otrace_on = -1; + + +bool is_otrace_on(void) +{ + return !!otrace_on; +} +static ssize_t otracer_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offp) +{ + char *kbuf = NULL; + + if (!is_otrace_on()) + return count; + + pr_info("otracer_write\n"); + kbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); + + if (kbuf == NULL) + goto end; + + if (!count) + goto free_buf; + + if (copy_from_user(kbuf, buf, ((count > PAGE_SIZE)?PAGE_SIZE:count))) + goto free_buf; + + oem_con_write(kbuf, ((count > PAGE_SIZE)?PAGE_SIZE:count)); + kfree(kbuf); + + return count; +free_buf: + kfree(kbuf); +end: + pr_info("otracer_write fail!\n"); + return -EFAULT; +} + +static long otracer_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + unsigned int cmdv = cmd; + int i; + + cmdv = cmd; + + pr_info("otracer_ioctl: initialized. cmd=0x%x\n", cmd); + + /* + * mwalker give a chance to change reboot + * result to android for android framework. + */ + if (cmd == IOCTL_TRACE_UPDATE_REBOOTFLAG) { + pr_info("android update reboot flag\n"); + goto end; + } + + for (i = 0; i < 16; i++) { + if (cmdv == 0) + break; + + if (cmdv & IOCTL_OTRACER_TOLCD) + cmdv &= (~IOCTL_OTRACER_TOLCD); + + if (cmdv & IOCTL_OTRACER_STACK) { + backtrace_test_saved(); + cmdv &= (~IOCTL_OTRACER_STACK); + } + if (cmdv & IOCTL_OTRACER_MEMINFO) { + meminfo_test_saved(); + cmdv &= (~IOCTL_OTRACER_MEMINFO); + } + if (cmdv & IOCTL_OTRACER_TASKINFO) { + tasks_test_saved(); + cmdv &= (~IOCTL_OTRACER_TASKINFO); + } + if (cmdv & IOCTL_OTRACER_ALLINFO) { + backtrace_test_saved(); + meminfo_test_saved(); + tasks_test_saved(); + cmdv &= (~IOCTL_OTRACER_ALLINFO); + } + if (cmdv & IOCTL_OTRACER_PANIC) { + pr_info("ioctl panic reboot\n"); + panic("android"); + cmdv &= (~IOCTL_OTRACER_PANIC); + } + } +end: + return 0; +} +static int otracer_open(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + return nonseekable_open(inode, file); +} +static int otracer_close(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + return 0; +} +static const struct file_operations otracer_fops = { + .owner = THIS_MODULE, + .open = otracer_open, + .release = otracer_close, + .read = otracer_read, + .write = otracer_write, + .unlocked_ioctl = otracer_ioctl, +}; + +static struct miscdevice otracer_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "otracer", + .fops = &otracer_fops, +}; +static int otrace_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "\notrace_on:%d\n", is_otrace_on()); + return 0; +} +static int otrace_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, otrace_proc_show, NULL); +} +static ssize_t otrace_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + unsigned char *lbuf; + size_t local_count; + unsigned long val; + ssize_t ret; + + if (count <= 0) + return 0; + +#define LBUFSIZE 1200UL + lbuf = kmalloc(LBUFSIZE, GFP_KERNEL); + if (!lbuf) + return 0; + + local_count = (LBUFSIZE - 1) > count?count:(LBUFSIZE - 1); + if (copy_from_user(lbuf, buffer, local_count) != 0) { + kfree(lbuf); + return -EFAULT; + } + + ret = kstrtoul(lbuf, 10, &val); + if (ret) + return ret; + + if (val == 7978) + otrace_on = 1; + else + otrace_on = 0; + + pr_info("val:%ld, otrace_on:%d\n", val, otrace_on); + + kfree(lbuf); + return count; +} + + + +static const struct file_operations otrace_proc_fops = { + .owner = THIS_MODULE, + .open = otrace_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = otrace_proc_write, +}; + +static struct proc_dir_entry *otrace_entry; +static int __init otracer_init(void) +{ + int ret; + + ret = misc_register(&otracer_misc); + if (unlikely(ret)) { + pr_err("otracer: failed to register misc device!\n"); + return ret; + } + + /* Set up the proc file system */ + otrace_entry = proc_create("otrace_on", 0644, NULL, &otrace_proc_fops); + if (!otrace_entry) { + ret = -ENOMEM; + goto out_misc; + } + + pr_info("otracer: initialized\n"); + + return 0; +out_misc: + misc_deregister(&otracer_misc); + return ret; +} + +static void __exit otracer_exit(void) +{ + + remove_proc_entry("otrace_on", NULL); + + misc_deregister(&otracer_misc); + + pr_info("otracer: exit\n"); +} + +module_init(otracer_init); +module_exit(otracer_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("oem tracer"); +MODULE_AUTHOR("Andy"); + + diff --git a/drivers/oneplus/oem_trace.h b/drivers/oneplus/oem_trace.h new file mode 100644 index 000000000000..f0013241b126 --- /dev/null +++ b/drivers/oneplus/oem_trace.h @@ -0,0 +1,9 @@ +#ifndef _OEM_TRACE_H +#define _OEM_TRACE_H + +#include + +#define IOCTL_TRACE_UPDATE_REBOOTFLAG _IO('t', 3) + + +#endif diff --git a/drivers/param_read_write/Kconfig b/drivers/param_read_write/Kconfig new file mode 100644 index 000000000000..5527917c6708 --- /dev/null +++ b/drivers/param_read_write/Kconfig @@ -0,0 +1,6 @@ +config PARAM_READ_WRITE + bool "Param partition read/write support" + default n + help + if you want to read/write the param partition in kernel, + then you must say Y here. diff --git a/drivers/param_read_write/Makefile b/drivers/param_read_write/Makefile new file mode 100644 index 000000000000..a946668758d8 --- /dev/null +++ b/drivers/param_read_write/Makefile @@ -0,0 +1,3 @@ +#obj-$(CONFIG_PARAM_READ_WRITE) += param_read_write.o +obj-y += param_read_write.o + diff --git a/drivers/param_read_write/param_read_write.c b/drivers/param_read_write/param_read_write.c new file mode 100644 index 000000000000..34839e45b9e8 --- /dev/null +++ b/drivers/param_read_write/param_read_write.c @@ -0,0 +1,408 @@ +/* + * drivers/param_read_write/param_read_write.c + * + * hefaxi@filesystems,2015/04/30 + * + * This program is used to read/write param partition in kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PARAM_PARTITION "/dev/block/bootdevice/by-name/param" +#define READ_CHUNK_MAX_SIZE (1024) +#define WRITE_CHUNK_MAX_SIZE (1024) +static uint default_param_data_dump_size= DEFAULT_PARAM_DUMP_SIZE; + +typedef struct{ + phys_addr_t paddr; + size_t size; + void *vaddr; + void *buffer; + struct mutex mutex; +}param_ram_zone_t; + +static DEFINE_MUTEX(param_lock); +static bool param_init_done = 0; +static param_ram_zone_t param_ram_zone; + +static int write_param_partition(const char *buf, unsigned long count, + loff_t offset) +{ + struct file *filp; + mm_segment_t fs; + int ret = 0; + + filp = filp_open(PARAM_PARTITION,O_RDWR|O_SYNC,0); + if(IS_ERR(filp)) { + ret = PTR_ERR(filp); + pr_err("open file %s failed.(%d)\n",PARAM_PARTITION,ret); + return ret; + } + + fs = get_fs(); + set_fs(get_ds()); + + ret = filp->f_op->llseek(filp, offset, SEEK_SET); + if(ret < 0){ + pr_err("%s: llseek failed.(%d)\n",__func__,ret); + goto out; + } + //ret = filp->f_op->write(filp,(char __user *)buf,count,&filp->f_pos); + ret = __vfs_write(filp, (char __user *)buf, count, &filp->f_pos); + +out: + set_fs(fs); + filp_close(filp,NULL); + return ret; +} + +int get_param_by_index_and_offset(uint32 sid_index, + uint32 offset, void * buf, int length) +{ + int ret = length; + uint32 file_offset; + mutex_lock(¶m_ram_zone.mutex); + pr_info("%s[%d] sid_index = %d offset = %d buf = %p length = %d\n", + __func__, __LINE__,sid_index,offset,buf,length); + + file_offset = PARAM_SID_LENGTH*sid_index+ offset; + + if (buf && ((offset + length) <= PARAM_SID_LENGTH) && + (file_offset + length) <= param_ram_zone.size) + memcpy(buf,(param_ram_zone.buffer +file_offset), length); + else{ + pr_info("%s:invaild argument, sid_index=%d offset=%d buf=%p length=%d\n", + __func__, sid_index, offset, buf, length); + ret = -EINVAL; + } + + mutex_unlock(¶m_ram_zone.mutex); + return ret; +} +EXPORT_SYMBOL(get_param_by_index_and_offset); + +int set_param_by_index_and_offset(uint32 sid_index, + uint32 offset, void * buf, int length) +{ + int ret; + uint32 file_offset; + mutex_lock(¶m_ram_zone.mutex); + pr_info("%s[%d]sid_index = %d offset = %d buf = %p length = %d\n", + __func__, __LINE__,sid_index,offset,buf,length); + + file_offset = PARAM_SID_LENGTH*sid_index + offset; + + if (buf && ((offset + length) <= PARAM_SID_LENGTH) && + (file_offset + length) <= param_ram_zone.size) + memcpy((param_ram_zone.buffer+file_offset),buf,length); + else{ + pr_info("%s:invaild argument,sid_index=%d offset=%d buf=%p length=%d\n", + __func__,sid_index,offset,buf,length); + ret = -EINVAL; + goto out; + } + + ret = write_param_partition((param_ram_zone.buffer+file_offset), + length,file_offset); + if ( ret < 0){ + pr_info("Error write param partition.(%d)\n",ret); + } +out: + mutex_unlock(¶m_ram_zone.mutex); + return ret; +} +EXPORT_SYMBOL(set_param_by_index_and_offset); + +static void *persistent_ram_vmap(phys_addr_t start, size_t size) +{ + struct page **pages; + phys_addr_t page_start; + unsigned int page_count; + pgprot_t prot; + unsigned int i; + void *vaddr; + + page_start = start - offset_in_page(start); + page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); + + /* prot = pgprot_noncached(PAGE_KERNEL); */ + prot = pgprot_writecombine(PAGE_KERNEL); + + pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL); + if (!pages) { + pr_err("%s: Failed to allocate array for %u pages\n", __func__, + page_count); + return NULL; + } + + for (i = 0; i < page_count; i++) { + phys_addr_t addr = page_start + i * PAGE_SIZE; + pages[i] = pfn_to_page(addr >> PAGE_SHIFT); + } + vaddr = vmap(pages, page_count, VM_MAP, prot); + kfree(pages); + return vaddr; +} + +static int param_ram_buffer_map(phys_addr_t start, phys_addr_t size, + param_ram_zone_t *prz) +{ + prz->paddr = start; + prz->size = size; + prz->vaddr = persistent_ram_vmap(start, size); + + if (!prz->vaddr) { + pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, + (unsigned long long)size, (unsigned long long)start); + return -ENOMEM; + } + + prz->buffer = prz->vaddr + offset_in_page(start); + return 0; +} + +static ssize_t param_read(struct file *file, char __user *buff, + size_t count, loff_t *pos) +{ + void * temp_buffer; + int chunk_sz; + int copied; + int left; + int ret; + + if (mutex_lock_interruptible(¶m_lock)) + return -ERESTARTSYS; + + chunk_sz = count < READ_CHUNK_MAX_SIZE ? count : READ_CHUNK_MAX_SIZE; + temp_buffer = kzalloc(chunk_sz, GFP_KERNEL); + + if (temp_buffer == NULL) + return -ENOMEM; + + left = count; + copied = 0; + + while (left) { + chunk_sz = (left <= READ_CHUNK_MAX_SIZE) ? + left : READ_CHUNK_MAX_SIZE; + ret = get_param_by_index_and_offset(*pos/PARAM_SID_LENGTH, + *pos%PARAM_SID_LENGTH, temp_buffer, chunk_sz); + + if (ret < 0) { + pr_err("get_param_by_index_and_offset fail %d\n", ret); + goto out; + } + + if (copy_to_user(buff + copied, temp_buffer, chunk_sz)) { + ret = -EFAULT; + pr_info("copy_to_user failure\n"); + goto out; + } + + *pos += chunk_sz; + left -= chunk_sz; + copied += chunk_sz; + } + +out: + kfree(temp_buffer); + mutex_unlock(¶m_lock); + return copied; +} + +static ssize_t param_write(struct file *file, const char __user *buff, + size_t count, loff_t *pos) +{ + + void * temp_buffer; + int chunk_sz; + int written; + int left; + int ret; + if (mutex_lock_interruptible(¶m_lock)) + return -ERESTARTSYS; + + chunk_sz = count < WRITE_CHUNK_MAX_SIZE ? count : WRITE_CHUNK_MAX_SIZE; + temp_buffer = kzalloc(chunk_sz, GFP_KERNEL); + + if (temp_buffer == NULL) + return -ENOMEM; + + left = count; + written = 0; + + while (left > 0) { + chunk_sz = (left <= WRITE_CHUNK_MAX_SIZE) ? + left : WRITE_CHUNK_MAX_SIZE; + ret = copy_from_user(temp_buffer, buff + written, chunk_sz); + if (ret < 0) { + pr_info("copy_from_user failure %d\n", ret); + goto out; + } + + ret = set_param_by_index_and_offset(*pos / PARAM_SID_LENGTH, + *pos % PARAM_SID_LENGTH, temp_buffer, chunk_sz); + + if (ret < 0) { + pr_err("set_param_by_index_and_offset failure %d\n", + ret); + goto out; + } + + *pos += chunk_sz; + left -= chunk_sz; + written += chunk_sz; + } +out: + kfree(temp_buffer); + mutex_unlock(¶m_lock); + return written; +} + +static const struct file_operations param_fops = { + .owner = THIS_MODULE, + .read = param_read, + .write = param_write, + .llseek = default_llseek, +}; + +struct miscdevice param_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "param", + .fops = ¶m_fops, +}; + +static int __init param_core_init(void) +{ + int i; + + if(param_ram_buffer_map((phys_addr_t)param_ram_zone.paddr, + param_ram_zone.size, (param_ram_zone_t *)¶m_ram_zone)){ + pr_err("param_ram_buffer_map failred\n"); + return -1; + } + mutex_init(¶m_ram_zone.mutex); + for (i = 0; i < NUM_PARAM_PLAINTEXT_SEGMENT; i++) { + break;//do not dump param + printk("===dump chunk %d===\n", i); + print_hex_dump (KERN_ERR, "",DUMP_PREFIX_OFFSET,16, 4, + param_ram_zone.buffer +1024*i, default_param_data_dump_size,1); + } + + param_init_done= 1; + return 0; +} +pure_initcall(param_core_init); + +static int __init param_device_init(void) +{ + int ret; + ret = misc_register(¶m_misc); + if(ret){ + pr_err("misc_register failure %d\n",ret); + return -1; + } + return ret; +} +device_initcall(param_device_init); + +void init_param_mem_base_size(phys_addr_t base, unsigned long size) +{ + param_ram_zone.paddr = base; + param_ram_zone.size = size; +} +EXPORT_SYMBOL(init_param_mem_base_size); + +/* +*Add more function here +* +*/ + +int restart_08_count; +int add_restart_08_count(void) +{ + int ret; + + ret = get_param_by_index_and_offset(9, 0x15c, + &restart_08_count, sizeof(restart_08_count)); + + restart_08_count = restart_08_count + 1; + + ret = set_param_by_index_and_offset(9, 0x15c, + &restart_08_count, sizeof(restart_08_count)); + + if (ret < 0) + pr_info("%s[%d] failed!\n", __func__, __LINE__); + + return ret; +} +EXPORT_SYMBOL(add_restart_08_count); + +static int param_get_restart_08_count(char *val, struct kernel_param *kp) +{ + + int cnt = 0; + int ret; + + ret = get_param_by_index_and_offset(9, 0x15c, + &restart_08_count, sizeof(restart_08_count)); + + if (ret < 0) + pr_info("%s[%d] failed!\n", __func__, __LINE__); + + cnt = snprintf(val, 4, "%d", restart_08_count); + + return cnt; +} +module_param_call(restart_08_count, NULL, param_get_restart_08_count, &restart_08_count, 0644); + +int restart_other_count=0; +int add_restart_other_count(void) +{ + int ret; + + ret = get_param_by_index_and_offset(9, 0x160, + &restart_other_count, sizeof(restart_other_count)); + + restart_other_count = restart_other_count + 1; + + ret = set_param_by_index_and_offset(9, 0x160, + &restart_other_count, sizeof(restart_other_count)); + + if (ret < 0) + pr_info("%s[%d] failed!\n", __func__, __LINE__); + + return ret; +} +EXPORT_SYMBOL(add_restart_other_count); +static int param_get_restart_other_count(char *val, struct kernel_param *kp) +{ + + int cnt = 0; + int ret; + + ret = get_param_by_index_and_offset(9, 0x160, + &restart_other_count, sizeof(restart_other_count)); + + if (ret < 0) + pr_info("%s[%d] failed!\n", __func__, __LINE__); + + cnt = snprintf(val, 4, "%d", restart_other_count); + + return cnt; +} +module_param_call(restart_other_count, NULL, param_get_restart_other_count, &restart_other_count, 0644); +//end diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index b180e67acafb..b5ab01ace04f 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -6940,27 +6940,6 @@ void msm_pcie_fixup_resume(struct pci_dev *dev) DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_resume); -void msm_pcie_fixup_resume_early(struct pci_dev *dev) -{ - int ret; - struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus); - - PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx); - - if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) || - pcie_dev->user_suspend) - return; - - mutex_lock(&pcie_dev->recovery_lock); - ret = msm_pcie_pm_resume(dev, NULL, NULL, 0); - if (ret) - PCIE_ERR(pcie_dev, "PCIe: RC%d got failure in resume:%d.\n", - pcie_dev->rc_idx, ret); - - mutex_unlock(&pcie_dev->recovery_lock); -} -DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, - msm_pcie_fixup_resume_early); int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, void *data, u32 options) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 295bf1472d02..500ee71f4747 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -637,12 +637,9 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) break; } - /* enter specified state */ - pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); - - /* Mandatory power management transition delays */ - /* see PCI PM 1.1 5.6.1 table 18 */ - if (state == PCI_D3hot || dev->current_state == PCI_D3hot) + if ((pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr) + != PCIBIOS_DEVICE_NOT_FOUND) && + (state == PCI_D3hot || dev->current_state == PCI_D3hot)) pci_dev_d3_sleep(dev); else if (state == PCI_D2 || dev->current_state == PCI_D2) udelay(PCI_PM_D2_DELAY); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 7b36cc4ad110..a7a549f5e9bf 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -30,13 +30,21 @@ #include #include #include +#include #include "../core.h" #include "../pinconf.h" #include "pinctrl-msm.h" #include "../pinctrl-utils.h" +#include +#include +#include #define MAX_NR_GPIO 300 #define PS_HOLD_OFFSET 0x820 +static int resume_wakeup_flag = 0; +bool fp_irq_cnt; + +bool need_show_pinctrl_irq; /** * struct msm_pinctrl - state for a pinctrl-msm device @@ -440,6 +448,20 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(g->in_bit)); } +/*2017-08-22 add for dash adapter update*/ +static int msm_gpio_get_dash(struct gpio_chip *chip, unsigned offset) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = container_of(chip, + struct msm_pinctrl, chip); + u32 val; + + /*pr_err("%s enter\n", __func__);*/ + g = &pctrl->soc->groups[offset]; + + val = readl_dash(pctrl->regs + g->io_reg); + return !!(val & BIT(g->in_bit)); +} static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { const struct msm_pingroup *g; @@ -460,6 +482,27 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_unlock_irqrestore(&pctrl->lock, flags); } +/*2017-08-22 add for dash adapter update*/ +static void msm_gpio_set_dash(struct gpio_chip *chip, + unsigned offset, int value) +{ + const struct msm_pingroup *g; + struct msm_pinctrl *pctrl = container_of(chip, + struct msm_pinctrl, chip); + u32 val; + + /*pr_err("%s enter\n", __func__);*/ + g = &pctrl->soc->groups[offset]; + + /*spin_lock_irqsave(&pctrl->lock, flags);*/ + if (value) + val = BIT(g->out_bit); + else + val = ~BIT(g->out_bit); + writel_dash(val, pctrl->regs + g->io_reg); + + /*spin_unlock_irqrestore(&pctrl->lock, flags);*/ +} #ifdef CONFIG_DEBUG_FS #include @@ -504,6 +547,11 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned i; for (i = 0; i < chip->ngpio; i++, gpio++) { + if (gpio == 0 || gpio == 1 || + gpio == 2 || gpio == 3 || + gpio == 81 || gpio == 82 || + gpio == 83 || gpio == 84) + continue; msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); seq_puts(s, "\n"); } @@ -517,7 +565,11 @@ static struct gpio_chip msm_gpio_template = { .direction_input = msm_gpio_direction_input, .direction_output = msm_gpio_direction_output, .get = msm_gpio_get, +/*2017-08-22 add for dash adapter update*/ + .get_dash = msm_gpio_get_dash, .set = msm_gpio_set, +/*2017-08-22 add for dash adapter update*/ + .set_dash = msm_gpio_set_dash, .request = gpiochip_generic_request, .free = gpiochip_generic_free, .dbg_show = msm_gpio_dbg_show, @@ -766,6 +818,38 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; +static void init_resume_wakeup_flag(void) +{ + resume_wakeup_flag = 0; +} + +static int is_speedup_irq(struct irq_desc *desc, char *irq_name) +{ + return strstr(desc->action->name, irq_name) != NULL; +} + +static void set_resume_wakeup_flag(int irq) +{ + struct irq_desc *desc; + desc = irq_to_desc(irq); + + if (desc && desc->action && desc->action->name) { + if (is_speedup_irq(desc, "synaptics,s3320")) + resume_wakeup_flag = 1; + } +} + +int get_resume_wakeup_flag(void) +{ + int flag = resume_wakeup_flag; + + pr_debug("%s: flag = %d\n", __func__, flag); + /* Clear it for next calling */ + init_resume_wakeup_flag(); + + return flag; +} + static void msm_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); @@ -776,9 +860,11 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) int handled = 0; u32 val; int i; + char irq_name[16] = {0}; chained_irq_enter(chip, desc); + init_resume_wakeup_flag(); /* * Each pin has it's own IRQ status register, so use * enabled_irq bitmap to limit the number of reads. @@ -790,6 +876,23 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) irq_pin = irq_find_mapping(gc->irqdomain, i); generic_handle_irq(irq_pin); handled++; + /* ++add by lyb@bsp for printk wakeup irqs */ + if (!!need_show_pinctrl_irq) { + need_show_pinctrl_irq = false; + strlcpy(irq_name, + irq_to_desc(irq_pin)->action->name, 16); + if (strnstr(irq_name, + "soc:fpc_fpc1020", 16) != NULL || + strnstr(irq_name, "gf_fp", 6) != NULL) { + fp_irq_cnt = true; + c0_cpufreq_limit_queue(); + } + set_resume_wakeup_flag(irq_pin); + pr_warn("hwirq %s [irq_num=%d ]triggered\n", + irq_to_desc(irq_pin)->action->name, irq_pin); + log_wakeup_reason(irq_pin); + } + /* -- */ } } @@ -899,6 +1002,26 @@ static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl) } } +static int pm_pm_event(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + switch (pm_event) { + case PM_SUSPEND_PREPARE: + /* do nothing */ + break; + case PM_POST_SUSPEND: + need_show_pinctrl_irq = false; + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block pinctrl_pm_notifier_block = { + .notifier_call = pm_pm_event, +}; + #ifdef CONFIG_PM static int msm_pinctrl_suspend(void) { @@ -951,7 +1074,10 @@ int msm_pinctrl_probe(struct platform_device *pdev, struct msm_pinctrl *pctrl; struct resource *res; int ret; - + ret = register_pm_notifier(&pinctrl_pm_notifier_block); + if (ret) + pr_warn("[%s] failed to register PM notifier %d\n", + __func__, ret); msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); if (!pctrl) { @@ -1008,6 +1134,7 @@ int msm_pinctrl_remove(struct platform_device *pdev) gpiochip_remove(&pctrl->chip); pinctrl_unregister(pctrl->pctrl); + unregister_pm_notifier(&pinctrl_pm_notifier_block); unregister_restart_handler(&pctrl->restart_nb); unregister_syscore_ops(&msm_pinctrl_pm_ops); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index e986fdacc0bf..e18c52725d90 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -122,4 +122,6 @@ int msm_pinctrl_probe(struct platform_device *pdev, int msm_pinctrl_remove(struct platform_device *pdev); extern int msm_show_resume_irq_mask; +extern bool need_show_pinctrl_irq; + #endif diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index 341bb0cd7c77..ae220912dfcf 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c @@ -785,12 +785,15 @@ int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in, } ep = &ipa3_ctx->ep[ipa_ep_idx]; - + /* + * modify by xcb for avoid wlan ep are not clear + * at last time unload wlan module. + */ if (ep->valid) { - IPAERR("EP already allocated.\n"); - goto fail; + IPAERR("EP %d already allocated!!!\n", ipa_ep_idx); + /* goto fail; delete by xcb */ + ipa3_disconnect_wdi_pipe(ipa_ep_idx); } - memset(&ipa3_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa3_ep_context)); IPA_ACTIVE_CLIENTS_INC_EP(in->sys.client); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 1974d6ee032b..dfd9a2356369 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -47,7 +47,10 @@ static ssize_t power_supply_show_property(struct device *dev, "Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP", "USB_CDP", "USB_ACA", "USB_HVDCP", "USB_HVDCP_3", "USB_PD", "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "Wipower", - "TYPEC", "TYPEC_UFP", "TYPEC_DFP" + "TYPEC", "TYPEC_UFP", "TYPEC_DFP", "DASH" + }; + static const char *const cc_orientation_text[] = { + "Unknown", "cc1", "cc2" }; static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" @@ -123,6 +126,10 @@ static ssize_t power_supply_show_property(struct device *dev, return sprintf(buf, "%s\n", typec_text[value.intval]); else if (off == POWER_SUPPLY_PROP_TYPEC_POWER_ROLE) return sprintf(buf, "%s\n", typec_pr_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION) + return snprintf( + buf, 255, "%s\n", + cc_orientation_text[value.intval]); else if (off == POWER_SUPPLY_PROP_DIE_HEALTH) return sprintf(buf, "%s\n", health_text[value.intval]); else if (off == POWER_SUPPLY_PROP_CONNECTOR_HEALTH) @@ -163,6 +170,23 @@ static ssize_t power_supply_store_property(struct device *dev, static struct device_attribute power_supply_attrs[] = { /* Properties of type `int' */ POWER_SUPPLY_ATTR(status), + POWER_SUPPLY_ATTR(set_allow_read_extern_fg_iic), + POWER_SUPPLY_ATTR(cc_to_cv_point), + POWER_SUPPLY_ATTR(chg_protect_status), + POWER_SUPPLY_ATTR(fastchg_status), + POWER_SUPPLY_ATTR(fastchg_starting), + POWER_SUPPLY_ATTR(cutoff_volt_with_charger), + POWER_SUPPLY_ATTR(update_lcd_is_off), + POWER_SUPPLY_ATTR(check_usb_unplug), + POWER_SUPPLY_ATTR(otg_switch), + POWER_SUPPLY_ATTR(switch_dash), + POWER_SUPPLY_ATTR(notify_charger_set_parameter), + POWER_SUPPLY_ATTR(fg_capacity), + POWER_SUPPLY_ATTR(fg_current_now), + POWER_SUPPLY_ATTR(fg_voltage_now), + POWER_SUPPLY_ATTR(is_aging_test), + POWER_SUPPLY_ATTR(battery_health), + POWER_SUPPLY_ATTR(bq_soc), POWER_SUPPLY_ATTR(charge_type), POWER_SUPPLY_ATTR(health), POWER_SUPPLY_ATTR(present), @@ -277,6 +301,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(current_capability), POWER_SUPPLY_ATTR(typec_mode), POWER_SUPPLY_ATTR(typec_cc_orientation), + POWER_SUPPLY_ATTR(oem_cc_orientation),/* xiangling */ POWER_SUPPLY_ATTR(typec_power_role), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 7569e35b59e0..9ef57ce94192 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -94,6 +94,16 @@ config POWER_RESET_QCOM help Power off and restart support for Qualcomm boards. +#ifdef VENDOR_EIDT +config DLOAD_MODE_DEFAULT + bool "Qualcomm download mode default" + depends on POWER_RESET_QCOM + help + This makes the SoC enter download mode when it resets + due to a kernel panic. Note that this doesn't by itself + make the kernel reboot on a kernel panic - that must be + enabled via another mechanism. +#endif config QCOM_DLOAD_MODE bool "Qualcomm download mode" depends on POWER_RESET_QCOM diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index d052e9518060..09ade6adbfe9 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -72,7 +72,11 @@ static void scm_disable_sdi(void); static int in_panic; static int dload_type = SCM_DLOAD_FULLDUMP; +#ifdef CONFIG_DLOAD_MODE_DEFAULT +static int download_mode = -1; +#else static int download_mode = 1; +#endif static struct kobject dload_kobj; static void *dload_mode_addr, *dload_type_addr; static bool dload_mode_enabled; @@ -100,6 +104,11 @@ struct reset_attribute { module_param_call(download_mode, dload_set, param_get_int, &download_mode, 0644); +int oem_get_download_mode(void) +{ + return download_mode; +} + static int panic_prep_restart(struct notifier_block *this, unsigned long event, void *ptr) { @@ -137,7 +146,7 @@ int scm_set_dload_mode(int arg1, int arg2) static void set_dload_mode(int on) { int ret; - + pr_err("set_dload_mode %s\n", on ? "ON" : "OFF"); if (dload_mode_addr) { __raw_writel(on ? 0xE47B337D : 0, dload_mode_addr); __raw_writel(on ? 0xCE14091A : 0, @@ -148,6 +157,12 @@ static void set_dload_mode(int on) ret = scm_set_dload_mode(on ? dload_type : 0, 0); if (ret) pr_err("Failed to set secure DLOAD mode: %d\n", ret); + if (on) + qpnp_pon_set_restart_reason(0x00); + else + qpnp_pon_set_restart_reason(PON_RESTART_REASON_PANIC); + if (!on) + scm_disable_sdi(); dload_mode_enabled = on; } @@ -271,6 +286,7 @@ static void halt_spmi_pmic_arbiter(void) static void msm_restart_prepare(const char *cmd) { bool need_warm_reset = false; + bool oem_panic_record = false; #ifdef CONFIG_QCOM_DLOAD_MODE @@ -281,6 +297,7 @@ static void msm_restart_prepare(const char *cmd) set_dload_mode(download_mode && (in_panic || restart_mode == RESTART_DLOAD)); + #endif if (qpnp_pon_check_hard_reset_stored()) { @@ -293,16 +310,44 @@ static void msm_restart_prepare(const char *cmd) need_warm_reset = (get_dload_mode() || (cmd != NULL && cmd[0] != '\0')); } - + if (!download_mode && + (in_panic || restart_mode == RESTART_DLOAD)) { + oem_panic_record = true; + } + qpnp_pon_set_restart_reason(0x00); /* Hard reset the PMIC unless memory contents must be maintained. */ - if (need_warm_reset) { + if (need_warm_reset || oem_panic_record) { qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET); } else { qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); } if (cmd != NULL) { - if (!strncmp(cmd, "bootloader", 10)) { + if (!strncmp(cmd, "rf", 2)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_RF); + __raw_writel(RF_MODE, restart_reason); + } else if (!strncmp(cmd, "wlan", 4)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_WLAN); + __raw_writel(WLAN_MODE, restart_reason); + } else if (!strncmp(cmd, "mos", 3)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_MOS); + __raw_writel(MOS_MODE, restart_reason); + } else if (!strncmp(cmd, "ftm", 3)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_FACTORY); + __raw_writel(FACTORY_MODE, restart_reason); + } else if (!strncmp(cmd, "kernel", 6)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_KERNEL); + __raw_writel(KERNEL_MODE, restart_reason); + } else if (!strncmp(cmd, "modem", 5)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_MODEM); + __raw_writel(MODEM_MODE, restart_reason); + } else if (!strncmp(cmd, "android", 7)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_ANDROID); + __raw_writel(ANDROID_MODE, restart_reason); + } else if (!strncmp(cmd, "aging", 5)) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_AGING); + __raw_writel(AGING_MODE, restart_reason); + } else if (!strncmp(cmd, "bootloader", 10)) { qpnp_pon_set_restart_reason( PON_RESTART_REASON_BOOTLOADER); __raw_writel(0x77665500, restart_reason); @@ -357,6 +402,10 @@ static void msm_restart_prepare(const char *cmd) } } + if (oem_panic_record) { + qpnp_pon_set_restart_reason(PON_RESTART_REASON_PANIC); + __raw_writel(OEM_PANIC, restart_reason); + } flush_cache_all(); /*outer_flush_all is not supported by 64bit kernel*/ @@ -418,6 +467,7 @@ static void do_msm_poweroff(void) pr_notice("Powering off the SoC\n"); set_dload_mode(0); + qpnp_pon_set_restart_reason(0x00); scm_disable_sdi(); qpnp_pon_system_pwr_off(PON_POWER_OFF_SHUTDOWN); diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index b919c688e627..b6d670696326 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -1,4 +1,21 @@ menu "Qualcomm Technologies Inc Charger and Fuel Gauge support" +config FG_BQ27541 + tristate "TI bq27541 fg" + depends on I2C + help + Say Y here to enable the TI Fuel Gauge driver.This adds support + for battery fuel gauging and state of charge of battery connected to + the fuel gauge. The state of charge is reported through a BMS power + supply property and also sends uevents when the capacity is updated. + +config ONEPLUS_FASTCHG + tristate "ONEPLUS FAST CHARGE" + depends on I2C + help + Say Y here to enable the ONEPLUS FAST CHARGE driver.This adds support + for DASH charge and state of charge of battery connected to + the battery. The state of charge is reported through a battery power + supply property config QPNP_SMBCHARGER tristate "QPNP SMB Charger driver" @@ -80,6 +97,14 @@ config QPNP_SMB2 help Enables support for the SMB2 charging peripheral +config OP_DEBUG_CHG + tristate "OP DEBUG CHARGE" + depends on MFD_SPMI_PMIC + help + Enables support for the debug charging peripheral + When use agingboot,can dump pmic register and + print vbus vbat current information + a config SMB138X_CHARGER tristate "SMB138X Battery Charger" depends on MFD_I2C_PMIC diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index 92310ef5c803..afdf6771d23c 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += battery.o smb1351-charger.o pmic-voter.o obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o +obj-$(CONFIG_FG_BQ27541) += bq27541_fuelgauger.o +obj-$(CONFIG_ONEPLUS_FASTCHG) += op_dash_adapter.o oneplus_fastcg.o obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o obj-$(CONFIG_QPNP_SMB2) += step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o obj-$(CONFIG_SMB138X_CHARGER) += battery.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 6d5308b3dd0b..76df3f5e38bc 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -516,6 +516,7 @@ static int pl_fcc_vote_callback(struct votable *votable, void *data, if (chip->fcc_step_update) cancel_delayed_work_sync(&chip->fcc_step_update_work); + pr_info("total_fcc_ua=%d\n", total_fcc_ua); if (chip->pl_mode == POWER_SUPPLY_PL_NONE || get_effective_result_locked(chip->pl_disable_votable)) { @@ -804,6 +805,7 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, struct pl_data *chip = data; union power_supply_propval pval = {0, }; int rc = 0; + pr_info("%s,fv_uv=%d\n", __func__, fv_uv); if (fv_uv < 0) return 0; diff --git a/drivers/power/supply/qcom/battery_current_limit.c b/drivers/power/supply/qcom/battery_current_limit.c index 410e64321ba6..cc6dfbea902b 100644 --- a/drivers/power/supply/qcom/battery_current_limit.c +++ b/drivers/power/supply/qcom/battery_current_limit.c @@ -282,21 +282,27 @@ static void update_cpu_freq(void) } static void soc_mitigate(struct work_struct *work) -{ - if (bcl_hotplug_enabled) - queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); - update_cpu_freq(); -} - -static int get_and_evaluate_battery_soc(void) { static struct power_supply *batt_psy; union power_supply_propval ret = {0,}; int battery_percentage; enum bcl_threshold_state prev_soc_state; + static struct power_supply *usb_psy; + int usb_state; + bool is_usb_present; + + if (!usb_psy) + usb_psy = power_supply_get_by_name("usb"); + if (usb_psy) { + usb_state = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_PRESENT, &ret); + if (usb_state == 0) + is_usb_present = ret.intval; + } if (!batt_psy) batt_psy = power_supply_get_by_name("battery"); + if (batt_psy) { battery_percentage = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); @@ -305,16 +311,30 @@ static int get_and_evaluate_battery_soc(void) pr_debug("Battery SOC reported:%d", battery_soc_val); trace_bcl_sw_mitigation("SoC reported", battery_soc_val); prev_soc_state = bcl_soc_state; - bcl_soc_state = (battery_soc_val <= soc_low_threshold) ? + /*bcl_soc_state by usb status 2016.04.16*/ + pr_debug("is_usb_present:%d", is_usb_present); + if (is_usb_present) + bcl_soc_state = BCL_HIGH_THRESHOLD; + else + bcl_soc_state = (battery_soc_val <= soc_low_threshold) ? BCL_LOW_THRESHOLD : BCL_HIGH_THRESHOLD; + if (bcl_soc_state == prev_soc_state) - return NOTIFY_OK; + return; trace_bcl_sw_mitigation_event( (bcl_soc_state == BCL_LOW_THRESHOLD) ? "trigger SoC mitigation" : "clear SoC mitigation"); - schedule_work(&gbcl->soc_mitig_work); + + if (bcl_hotplug_enabled) + queue_work(gbcl->bcl_hotplug_wq, &bcl_hotplug_work); + update_cpu_freq(); } +} + +static int get_and_evaluate_battery_soc(void) +{ + schedule_work(&gbcl->soc_mitig_work); return NOTIFY_OK; } diff --git a/drivers/power/supply/qcom/bq27541_fuelgauger.c b/drivers/power/supply/qcom/bq27541_fuelgauger.c new file mode 100755 index 000000000000..de16184acfc6 --- /dev/null +++ b/drivers/power/supply/qcom/bq27541_fuelgauger.c @@ -0,0 +1,2000 @@ +/* Copyright (C) 2008 Rodolfo Giometti + * Copyright (C) 2008 Eurotech S.p.A. + * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "BQ: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#endif +#include +#include + +#define CONFIG_GAUGE_BQ27411 1 +#define DEVICE_TYPE_BQ27541 0x0541 +#define DEVICE_TYPE_BQ27411 0x0421 +#define DEVICE_BQ27541 0 +#define DEVICE_BQ27411 1 + +#define DRIVER_VERSION "1.1.0" +/* Bq27541 standard data commands */ +#define BQ27541_REG_CNTL 0x00 +#define BQ27541_REG_AR 0x02 +#define BQ27541_REG_ARTTE 0x04 +#define BQ27541_REG_TEMP 0x06 +#define BQ27541_REG_VOLT 0x08 +#define BQ27541_REG_FLAGS 0x0A +#define BQ27541_REG_NAC 0x0C +#define BQ27541_REG_FAC 0x0e +#define BQ27541_REG_RM 0x10 +#define BQ27541_REG_FCC 0x12 +#define BQ27541_REG_AI 0x14 +#define BQ27541_REG_TTE 0x16 +#define BQ27541_REG_TTF 0x18 +#define BQ27541_REG_SI 0x1a +#define BQ27541_REG_STTE 0x1c +#define BQ27541_REG_MLI 0x1e +#define BQ27541_REG_MLTTE 0x20 +#define BQ27541_REG_AE 0x22 +#define BQ27541_REG_AP 0x24 +#define BQ27541_REG_TTECP 0x26 +#define BQ27541_REG_SOH 0x28 +#define BQ27541_REG_SOC 0x2c +#define BQ27541_REG_NIC 0x2e +#define BQ27541_REG_ICR 0x30 +#define BQ27541_REG_LOGIDX 0x32 +#define BQ27541_REG_LOGBUF 0x34 + +#define BQ27541_FLAG_DSC BIT(0) +#define BQ27541_FLAG_FC BIT(9) + +#define BQ27541_CS_DLOGEN BIT(15) +#define BQ27541_CS_SS BIT(13) + +#ifdef CONFIG_GAUGE_BQ27411 +/* Bq27411 standard data commands */ +#define BQ27411_REG_TEMP 0x02 +#define BQ27411_REG_VOLT 0x04 +#define BQ27411_REG_RM 0x0c +#define BQ27411_REG_AI 0x10 +#define BQ27411_REG_SOC 0x1c +#define BQ27411_REG_HEALTH 0x20 + +#define CONTROL_CMD 0x00 +#define CONTROL_STATUS 0x00 +#define SEAL_POLLING_RETRY_LIMIT 100 +#define BQ27541_UNSEAL_KEY 0x11151986 +#define BQ27411_UNSEAL_KEY 0x80008000 + +#define BQ27541_RESET_SUBCMD 0x0041 +#define BQ27411_RESET_SUBCMD 0x0042 +#define SEAL_SUBCMD 0x0020 + +#define BQ27411_CONFIG_MODE_POLLING_LIMIT 60 +#define BQ27411_CONFIG_MODE_BIT BIT(4) +#define BQ27411_BLOCK_DATA_CONTROL 0x61 +#define BQ27411_DATA_CLASS_ACCESS 0x003e +#define BQ27411_CC_DEAD_BAND_ID 0x006b +#define BQ27411_CC_DEAD_BAND_ADDR 0x42 +#define BQ27411_CHECKSUM_ADDR 0x60 +#define BQ27411_CC_DEAD_BAND_POWERUP_VALUE 0x11 +#define BQ27411_CC_DEAD_BAND_SHUTDOWN_VALUE 0x71 + +#define BQ27411_OPCONFIGB_ID 0x0040 +#define BQ27411_OPCONFIGB_ADDR 0x42 +#define BQ27411_OPCONFIGB_POWERUP_VALUE 0x07 +#define BQ27411_OPCONFIGB_SHUTDOWN_VALUE 0x0f + +#define BQ27411_DODATEOC_ID 0x0024 +#define BQ27411_DODATEOC_ADDR 0x48 +#define BQ27411_DODATEOC_POWERUP_VALUE 0x32 +#define BQ27411_DODATEOC_SHUTDOWN_VALUE 0x32 + +#endif + +/* BQ27541 Control subcommands */ +#define BQ27541_SUBCMD_CTNL_STATUS 0x0000 +#define BQ27541_SUBCMD_DEVCIE_TYPE 0x0001 +#define BQ27541_SUBCMD_FW_VER 0x0002 +#define BQ27541_SUBCMD_HW_VER 0x0003 +#define BQ27541_SUBCMD_DF_CSUM 0x0004 +#define BQ27541_SUBCMD_PREV_MACW 0x0007 +#define BQ27541_SUBCMD_CHEM_ID 0x0008 +#define BQ27541_SUBCMD_BD_OFFSET 0x0009 +#define BQ27541_SUBCMD_INT_OFFSET 0x000a +#define BQ27541_SUBCMD_CC_VER 0x000b +#define BQ27541_SUBCMD_OCV 0x000c +#define BQ27541_SUBCMD_BAT_INS 0x000d +#define BQ27541_SUBCMD_BAT_REM 0x000e +#define BQ27541_SUBCMD_SET_HIB 0x0011 +#define BQ27541_SUBCMD_CLR_HIB 0x0012 +#define BQ27541_SUBCMD_SET_SLP 0x0013 +#define BQ27541_SUBCMD_CLR_SLP 0x0014 +#define BQ27541_SUBCMD_FCT_RES 0x0015 +#define BQ27541_SUBCMD_ENABLE_DLOG 0x0018 +#define BQ27541_SUBCMD_DISABLE_DLOG 0x0019 +#define BQ27541_SUBCMD_SEALED 0x0020 +#define BQ27541_SUBCMD_ENABLE_IT 0x0021 +#define BQ27541_SUBCMD_DISABLE_IT 0x0023 +#define BQ27541_SUBCMD_CAL_MODE 0x0040 +#define BQ27541_SUBCMD_RESET 0x0041 +#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN (-2731) +#define BQ27541_INIT_DELAY ((HZ)*1) +#define SET_BQ_PARAM_DELAY_MS 6000 + + +/* Bq27411 sub commands */ +#define BQ27411_SUBCMD_CNTL_STATUS 0x0000 +#define BQ27411_SUBCMD_DEVICE_TYPE 0x0001 +#define BQ27411_SUBCMD_FW_VER 0x0002 +#define BQ27411_SUBCMD_DM_CODE 0x0004 +#define BQ27411_SUBCMD_CONFIG_MODE 0x0006 +#define BQ27411_SUBCMD_PREV_MACW 0x0007 +#define BQ27411_SUBCMD_CHEM_ID 0x0008 +#define BQ27411_SUBCMD_SET_HIB 0x0011 +#define BQ27411_SUBCMD_CLR_HIB 0x0012 +#define BQ27411_SUBCMD_SET_CFG 0x0013 +#define BQ27411_SUBCMD_SEALED 0x0020 +#define BQ27411_SUBCMD_RESET 0x0041 +#define BQ27411_SUBCMD_SOFTRESET 0x0042 +#define BQ27411_SUBCMD_EXIT_CFG 0x0043 + +#define BQ27411_SUBCMD_ENABLE_DLOG 0x0018 +#define BQ27411_SUBCMD_DISABLE_DLOG 0x0019 +#define BQ27411_SUBCMD_ENABLE_IT 0x0021 +#define BQ27411_SUBCMD_DISABLE_IT 0x0023 + +#define BQ27541_BQ27411_CMD_INVALID 0xFF + + +#define ERROR_SOC 33 +#define ERROR_BATT_VOL (3800 * 1000) +/* If the system has several batteries we need a different name for each + * of them... + */ +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_mutex); + +struct bq27541_device_info; +struct bq27541_access_methods { + int (*read)(u8 reg, int *rt_value, int b_single, + struct bq27541_device_info *di); +}; + +#ifdef CONFIG_GAUGE_BQ27411 +struct cmd_address { + u8 reg_temp; + u8 reg_volt; + u8 reg_rm; + u8 reg_ai; + u8 reg_soc; + u8 reg_helth; +}; +#endif + +struct bq27541_device_info { + struct device *dev; + int id; + struct bq27541_access_methods *bus; + struct i2c_client *client; + struct work_struct counter; + /* 300ms delay is needed after bq27541 is powered up + * and before any successful I2C transaction + */ + struct delayed_work hw_config; + struct delayed_work modify_soc_smooth_parameter; + struct delayed_work battery_soc_work; + struct wake_lock update_soc_wake_lock; + struct power_supply *batt_psy; + int saltate_counter; + /* Add for retry when config fail */ + int retry_count; + /* Add for get right soc when sleep long time */ + int soc_pre; + int batt_vol_pre; + int current_pre; + int health_pre; + unsigned long rtc_resume_time; + unsigned long rtc_suspend_time; + atomic_t suspended; + int temp_pre; + int lcd_off_delt_soc; + int t_count; + bool lcd_is_off; + bool allow_reading; + bool fastchg_started; + bool bq_present; + bool set_smoothing; + bool disable_calib_soc; + unsigned long lcd_off_time; + unsigned long soc_pre_time; + unsigned long soc_store_time; +#ifdef CONFIG_GAUGE_BQ27411 + int device_type; + struct cmd_address cmd_addr; + bool modify_soc_smooth; + bool already_modify_smooth; +#endif +}; + + +#include + +struct update_pre_capacity_data { + struct delayed_work work; + struct workqueue_struct *workqueue; + int suspend_time; +}; +static struct update_pre_capacity_data update_pre_capacity_data; +static void bq27411_modify_soc_smooth_parameter( + struct bq27541_device_info *di, bool is_powerup); + +static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd, + struct bq27541_device_info *di); +static int bq27541_average_current(struct bq27541_device_info *di); + +static int bq27541_read(u8 reg, int *rt_value, int b_single, + struct bq27541_device_info *di) +{ + return di->bus->read(reg, rt_value, b_single, di); +} + +/* + * Return the battery temperature in tenths of degree Celsius + * Or < 0 if something fails. + */ +static int bq27541_battery_temperature(struct bq27541_device_info *di) +{ + int ret; + int temp = 0; + int error_temp; + static int count; + + /* Add for get right*/ + /*soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) + return di->temp_pre + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN; + + if (di->allow_reading) { + +#ifdef CONFIG_GAUGE_BQ27411 + ret = bq27541_read(di->cmd_addr.reg_temp, + &temp, 0, di); +#else + ret = bq27541_read(BQ27541_REG_TEMP, &temp, 0, di); +#endif + /* Add for don't report battery*/ + /*not connect when reading error once. */ + if (ret) { + count++; + pr_err("error reading temperature\n"); + if (count > 1) { + count = 0; + /* Add for it report bad*/ + /*status when plug out battery */ + di->temp_pre = + -400 - ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN; + error_temp = -400; + return error_temp; + } else { + return di->temp_pre + +ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN; + } + } + count = 0; + } else { + return di->temp_pre + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN; + } + di->temp_pre = temp; + return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN; +} + +/* + * Return the battery Voltage in milivolts + * Or < 0 if something fails. + */ +static int bq27541_battery_voltage(struct bq27541_device_info *di) +{ + int ret; + int volt = 0; + + /* Add for get right soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) + return di->batt_vol_pre; + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + ret = bq27541_read(di->cmd_addr.reg_volt, + &volt, 0, di); +#else + ret = bq27541_read(BQ27541_REG_VOLT, &volt, 0, di); +#endif + if (ret) { + pr_err("error reading voltage,ret:%d\n", ret); + return ret; + } + } else { + return di->batt_vol_pre; + } + di->batt_vol_pre = volt * 1000; + return volt * 1000; +} + +static void bq27541_cntl_cmd(struct bq27541_device_info *di, + int subcmd) +{ + mutex_lock(&battery_mutex); + bq27541_i2c_txsubcmd(BQ27541_REG_CNTL, subcmd, di); + mutex_unlock(&battery_mutex); + +} + +/* + * i2c specific code + */ +static int bq27541_i2c_txsubcmd(u8 reg, unsigned short subcmd, + struct bq27541_device_info *di) +{ + struct i2c_msg msg; + unsigned char data[3]; + int ret; + + if (!di->client) + return -ENODEV; + + memset(data, 0, sizeof(data)); + data[0] = reg; + data[1] = subcmd & 0x00FF; + data[2] = (subcmd & 0xFF00) >> 8; + + msg.addr = di->client->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = data; + + ret = i2c_transfer(di->client->adapter, &msg, 1); + if (ret < 0) + return -EIO; + + return 0; +} + +static int bq27541_chip_config(struct bq27541_device_info *di) +{ + int flags = 0, ret = 0; + + bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS); + udelay(66); + ret = bq27541_read(BQ27541_REG_CNTL, &flags, 0, di); + if (ret < 0) { + pr_err("error reading register %02x ret = %d\n", + BQ27541_REG_CNTL, ret); + return ret; + } + udelay(66); + + bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_IT); + udelay(66); + + if (!(flags & BQ27541_CS_DLOGEN)) { + bq27541_cntl_cmd(di, BQ27541_SUBCMD_ENABLE_DLOG); + udelay(66); + } + + return 0; +} + +struct bq27541_device_info *bq27541_di; +static struct i2c_client *new_client; + +#define TEN_PERCENT 10 +#define SOC_SHUTDOWN_VALID_LIMITS 30 +#define TEN_MINUTES 600 +#define FIVE_MINUTES 300 +#define TWO_POINT_FIVE_MINUTES 150 +#define ONE_MINUTE 60 +#define TWENTY_MINUTES 1200 +#define TWENTY_PERCENT 20 +#define TWENTY_SECS 20 + + +#define CAPACITY_SALTATE_COUNTER_60 38 /* 40 1min */ +#define CAPACITY_SALTATE_COUNTER_95 78 /* 60 2.5min */ +#define CAPACITY_SALTATE_COUNTER_FULL 200 /* 150 120 5min */ +#define CAPACITY_SALTATE_COUNTER_CHARGING_TERM 30 /* 30 1min */ +#define CAPACITY_SALTATE_COUNTER 4 +#define CAPACITY_SALTATE_COUNTER_NOT_CHARGING 24 /* >=40sec */ +#define LOW_BATTERY_PROTECT_VOLTAGE 3250000 +#define CAPACITY_CALIBRATE_TIME_60_PERCENT 45 /* 45s */ +#define LOW_BATTERY_CAPACITY_THRESHOLD 20 + +static int get_current_time(unsigned long *now_tm_sec) +{ + struct rtc_time tm; + struct rtc_device *rtc; + int rc; + + rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); + if (rtc == NULL) { + pr_err("%s: unable to open rtc device (%s)\n", + __FILE__, CONFIG_RTC_HCTOSYS_DEVICE); + return -EINVAL; + } + + rc = rtc_read_time(rtc, &tm); + if (rc) { + pr_err("Error reading rtc device (%s) : %d\n", + CONFIG_RTC_HCTOSYS_DEVICE, rc); + goto close_time; + } + + rc = rtc_valid_tm(&tm); + if (rc) { + pr_err("Invalid RTC time (%s): %d\n", + CONFIG_RTC_HCTOSYS_DEVICE, rc); + goto close_time; + } + rtc_tm_to_time(&tm, now_tm_sec); + +close_time: + rtc_class_close(rtc); + return rc; +} + +int get_prop_pre_shutdown_soc(void) +{ + int soc_load; + + soc_load = load_soc(); + if (soc_load == -1) + return 50; + else + return soc_load; +} + +static int fg_soc_calibrate(struct bq27541_device_info *di, int soc) +{ + union power_supply_propval ret = {0,}; + unsigned int soc_calib; + unsigned long soc_current_time, time_last; + static bool first_enter; + static int charging_status, charging_status_pre; + bool chg_done; + int temp_region, vbat_mv, ibat_ma, soc_load, soc_temp, counter_temp = 0; + + if (false == first_enter) { + di->batt_psy = power_supply_get_by_name("battery"); + if (di->batt_psy) { + first_enter = true; + soc_load = load_soc(); + pr_info("soc=%d, soc_load=%d\n", soc, soc_load); + if (soc_load < 0) { + /* get last soc error */ + di->soc_pre = soc; + } else if (soc_load > 0 && soc_load < 100) { + if (abs(soc_load - soc) > SOC_SHUTDOWN_VALID_LIMITS) + di->soc_pre = soc; + else if (soc_load > soc) + di->soc_pre = soc_load - 1; + else + di->soc_pre = soc_load; + } else if (soc_load == 100 + && abs(soc_load - soc) > TEN_PERCENT) { + /* decrease soc when gap between soc_load and */ + /* real_soc is over 10% */ + di->soc_pre = soc_load - 1; + } else { + di->soc_pre = soc_load; + } + + if (!di->batt_psy) { + pr_err( + "batt_psy is absent, soc_pre=%d\n", + di->soc_pre); + return di->soc_pre; + } + /* store the soc when boot first time */ + get_current_time(&di->soc_pre_time); + clean_backup_soc_ex(); + } else { + return soc; + } + } + soc_temp = di->soc_pre; + + if (!di->batt_psy) { + soc_calib = soc; + goto out; + } + + ret.intval = get_charging_status(); + di->batt_vol_pre = bq27541_battery_voltage(di); + chg_done = get_oem_charge_done_status(); + temp_region = fuelgauge_battery_temp_region_get(); + if ((temp_region == BATT_TEMP_LITTLE_COOL + || temp_region == BATT_TEMP_COOL + || temp_region == BATT_TEMP_NORMAL + || temp_region == BATT_TEMP_PRE_NORMAL) + && chg_done) { + ret.intval = POWER_SUPPLY_STATUS_FULL; + } + + if (ret.intval == POWER_SUPPLY_STATUS_CHARGING + || ret.intval == POWER_SUPPLY_STATUS_FULL + || bq27541_di->fastchg_started) + charging_status = 1; + else + charging_status = 0; + + if (charging_status ^ charging_status_pre) { + if (charging_status_pre) { + get_current_time(&soc_current_time); + di->soc_store_time = + soc_current_time - di->soc_pre_time; + } + + get_current_time(&di->soc_pre_time); + if (!charging_status_pre && di->soc_store_time) + di->soc_pre_time -= di->soc_store_time; + charging_status_pre = charging_status; + di->saltate_counter = 0; + } + + get_current_time(&soc_current_time); + time_last = soc_current_time - di->soc_pre_time; + if (charging_status) { /* is charging */ + if (ret.intval == POWER_SUPPLY_STATUS_FULL) { + soc_calib = di->soc_pre; + if (di->soc_pre < 100 + && (temp_region == BATT_TEMP_LITTLE_COOL + || temp_region == BATT_TEMP_NORMAL + || temp_region == BATT_TEMP_PRE_NORMAL + || temp_region == BATT_TEMP_COOL)) { + if (time_last > TWENTY_SECS) + soc_calib = di->soc_pre + 1; + } + } else { + if (soc - di->soc_pre > 0) { + di->saltate_counter++; + if ((bq27541_di->fastchg_started + && time_last < 20) + || (!bq27541_di->fastchg_started + && time_last < 30)) + return di->soc_pre; + di->saltate_counter = 0; + soc_calib = di->soc_pre + 1; + } else if (soc < (di->soc_pre - 1)) { + di->saltate_counter++; + if (di->soc_pre == 100) { + counter_temp = + CAPACITY_SALTATE_COUNTER_FULL; + /* t>=5min */ + } else if (di->soc_pre > 95) { + counter_temp = + CAPACITY_SALTATE_COUNTER_95; + /* t>=2.5min */ + } else if (di->soc_pre > 60) { + counter_temp = + CAPACITY_SALTATE_COUNTER_60; + /* t>=1min */ + } else { + if (time_last > + CAPACITY_CALIBRATE_TIME_60_PERCENT + && (soc - di->soc_pre) < 0) + counter_temp = 0; + else + /* t>=40sec */ + counter_temp = + CAPACITY_SALTATE_COUNTER_NOT_CHARGING; + } + + /* avoid dead battery shutdown */ + if (di->batt_vol_pre <= + LOW_BATTERY_PROTECT_VOLTAGE + && di->batt_vol_pre > 2500 * 1000 + && di->soc_pre + <= LOW_BATTERY_CAPACITY_THRESHOLD) { + /* check again */ + vbat_mv = + bq27541_battery_voltage(di); + if (vbat_mv <= + LOW_BATTERY_PROTECT_VOLTAGE + && vbat_mv > 2500 * 1000) { + /* about 9s */ + counter_temp = + CAPACITY_SALTATE_COUNTER - 1; + } + } + + ibat_ma = bq27541_average_current(di); + /* don't allow soc down*/ + /*if chg current > -200mA */ + if (di->saltate_counter < counter_temp + || ibat_ma < -200 * 1000) + return di->soc_pre; + di->saltate_counter = 0; + + soc_calib = di->soc_pre - 1; + } else if ((soc == 0 && soc < di->soc_pre) + && di->soc_pre <= 2) { + di->saltate_counter++; + if (time_last > + CAPACITY_CALIBRATE_TIME_60_PERCENT + && (soc - di->soc_pre) < 0) + counter_temp = 0; + else + /* t>=40sec */ + counter_temp = + CAPACITY_SALTATE_COUNTER_NOT_CHARGING; + + if (di->saltate_counter < counter_temp) + return di->soc_pre; + di->saltate_counter = 0; + soc_calib = di->soc_pre - 1; + } else { + soc_calib = di->soc_pre; + } + } + } else { /* not charging */ + if ((soc < di->soc_pre) + || (di->batt_vol_pre <= LOW_BATTERY_PROTECT_VOLTAGE + && di->batt_vol_pre > 2500 * 1000)) { + if (di->soc_pre == 100) { + counter_temp = FIVE_MINUTES; + } else if (di->soc_pre >= 95) { + counter_temp = TWO_POINT_FIVE_MINUTES; + } else if (di->soc_pre >= 60) { + counter_temp = ONE_MINUTE; + } else { + if (time_last >= + CAPACITY_CALIBRATE_TIME_60_PERCENT + && (soc - di->soc_pre) < 0) + counter_temp = 0; + else + /* t>=40sec */ + counter_temp = + CAPACITY_SALTATE_COUNTER_NOT_CHARGING + + 20; + } + /* avoid dead battery shutdown */ + if (di->batt_vol_pre <= + LOW_BATTERY_PROTECT_VOLTAGE + && di->batt_vol_pre > 2500 * 1000 + && di->soc_pre <= + LOW_BATTERY_CAPACITY_THRESHOLD) { + /* check again */ + vbat_mv = bq27541_battery_voltage(di); + if (vbat_mv <= LOW_BATTERY_PROTECT_VOLTAGE + && vbat_mv > 2500 * 1000 && time_last > 9) + counter_temp = 0; + } + + if (time_last < counter_temp) + return di->soc_pre; + } + + if (soc < di->soc_pre) + soc_calib = di->soc_pre - 1; + else if (di->batt_vol_pre <= LOW_BATTERY_PROTECT_VOLTAGE + && di->batt_vol_pre > 2500 * 1000 + && di->soc_pre > 0 && time_last > 9) + soc_calib = di->soc_pre - 1; + else + soc_calib = di->soc_pre; + } + +out: + if (soc_calib > 100) + soc_calib = 100; + if (soc_calib < 0) + soc_calib = 0; + if (soc_calib == 0) { + if ((di->batt_vol_pre/1000) > 3400) + soc_calib = 1; + } + di->soc_pre = soc_calib; + + if (soc_temp != soc_calib) { + get_current_time(&di->soc_pre_time); + /* store when soc changed */ + power_supply_changed(di->batt_psy); + pr_info("soc:%d, soc_calib:%d, VOLT:%d, current:%d\n", + soc, soc_calib, bq27541_battery_voltage(di) / 1000, + bq27541_average_current(di) / 1000); + } + + return soc_calib; +} + + +static int bq27541_battery_soc( +struct bq27541_device_info *di, int suspend_time_ms) +{ + int ret; + int soc = 0; + int soc_delt = 0; + static int soc_pre; + bool fg_soc_changed = false; + + /* Add for get right soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) { + dev_warn(di->dev, + "di->suspended di->soc_pre=%d\n", di->soc_pre); + return di->soc_pre; + } + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + ret = bq27541_read(di->cmd_addr.reg_soc, + &soc, 0, di); +#else + ret = bq27541_read(BQ27541_REG_SOC, &soc, 0, di); +#endif + if (ret) { + pr_err("error reading soc=%d, ret:%d\n", soc, ret); + goto read_soc_err; + } + if (soc_pre != soc) + pr_err("bq27541_battery_soc = %d\n", soc); + + soc_pre = soc; + } else { + if (di->soc_pre) + return di->soc_pre; + else + return 0; + } + /* Add for get right soc when sleep long time */ + if (suspend_time_ms && di->lcd_is_off) { + if (soc < di->soc_pre) { + soc_delt = di->soc_pre - soc; + fg_soc_changed = (soc < TWENTY_PERCENT + || soc_delt > di->lcd_off_delt_soc + || suspend_time_ms > TEN_MINUTES); + pr_info("suspend_time_ms=%d,soc_delt=%d,di->lcd_off_delt_soc=%d\n", + suspend_time_ms, soc_delt, di->lcd_off_delt_soc); + if (fg_soc_changed) { + if (suspend_time_ms/TEN_MINUTES) { + di->soc_pre -= + (suspend_time_ms / TEN_MINUTES < soc_delt + ? suspend_time_ms / TEN_MINUTES : soc_delt); + } else { + di->soc_pre -= 1; + } + /* store when soc changed */ + get_current_time(&di->soc_pre_time); + power_supply_changed(di->batt_psy); + pr_err("system resume,soc:%d, soc_calib:%d,VOLT:%d,current:%d\n", + soc, di->soc_pre, + bq27541_battery_voltage(di) / 1000, + bq27541_average_current(di) / 1000); + } + } + goto read_soc_err; + } + if (di->disable_calib_soc) + return soc; + soc = fg_soc_calibrate(di, soc); + return soc; + +read_soc_err: + if (di->soc_pre) { + dev_warn(di->dev, + "read_soc_exit ,di->soc_pre=%d\n", di->soc_pre); + return di->soc_pre; + } else + return 0; +} + +static int bq27541_average_current(struct bq27541_device_info *di) +{ + int ret; + int curr = 0; + + /* Add for get right soc when sleep long time */ + if (atomic_read(&di->suspended) == 1) + return -di->current_pre; + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + ret = bq27541_read(di->cmd_addr.reg_ai, + &curr, 0, di); +#else + ret = bq27541_read(BQ27541_REG_AI, &curr, 0, di); +#endif + if (ret) { + pr_err("error reading current.\n"); + return ret; + } + } else { + return -di->current_pre; + } + /* negative current */ + if (curr & 0x8000) + curr = -((~(curr-1)) & 0xFFFF); + di->current_pre = 1000 * curr; + return -curr * 1000; +} + +static int bq27541_remaining_capacity(struct bq27541_device_info *di) +{ + int ret; + int cap = 0; + + if (di->allow_reading) { +#ifdef CONFIG_GAUGE_BQ27411 + ret = bq27541_read(di->cmd_addr.reg_rm, + &cap, 0, di); +#else + ret = bq27541_read(BQ27541_REG_RM, &cap, 0, di); +#endif + if (ret) { + pr_err("error reading capacity.\n"); + return ret; + } + } + + return cap; +} + +static int bq27541_batt_health(struct bq27541_device_info *di) +{ + int ret; + int health = 0; + + if (di->allow_reading) { + ret = bq27541_read(di->cmd_addr.reg_helth, + &health, 0, di); + if (ret) { + pr_err("error reading health\n"); + return ret; + } + if (di->device_type == DEVICE_BQ27411) + di->health_pre = (health & 0xFF); + else + di->health_pre = health; + } + + return di->health_pre; +} + +static int bq27541_get_battery_mvolts(void) +{ + return bq27541_battery_voltage(bq27541_di); +} + +static int bq27541_get_batt_remaining_capacity(void) +{ + return bq27541_remaining_capacity(bq27541_di); +} + +static int bq27541_get_batt_health(void) +{ + return bq27541_batt_health(bq27541_di); +} +static int bq27541_get_batt_bq_soc(void) +{ + int soc; + + bq27541_di->disable_calib_soc = true; + soc = bq27541_battery_soc(bq27541_di, 0); + bq27541_di->disable_calib_soc = false; + return soc; +} +#define SHUTDOWN_TBAT 680 +static int bq27541_get_battery_temperature(void) +{ + int ret; + static unsigned long pre_time; + unsigned long current_time, time_last; + + ret = bq27541_battery_temperature(bq27541_di); + if (ret >= SHUTDOWN_TBAT) { + bq27541_di->t_count++; + if (bq27541_di->t_count == 1) + get_current_time(&pre_time); + get_current_time(¤t_time); + time_last = current_time - pre_time; + if (time_last < 8) + return SHUTDOWN_TBAT - 1; + else { + pr_info("Tbat =%d T_tol=%d\n", + ret, (int)(current_time - pre_time)); + } + } + bq27541_di->t_count = 0; + return ret; +} +static bool bq27541_is_battery_present(void) +{ + return true; +} + +static bool bq27541_is_battery_temp_within_range(void) +{ + return true; +} + +static bool bq27541_is_battery_id_valid(void) +{ + return true; +} + +#ifdef CONFIG_GAUGE_BQ27411 +static int bq27541_get_device_type(void) +{ + if (bq27541_di) + return bq27541_di->device_type; + + return 0; +} +#endif + +static int bq27541_get_battery_soc(void) +{ + return bq27541_battery_soc(bq27541_di, 0); +} + +static int bq27541_get_average_current(void) +{ + return bq27541_average_current(bq27541_di); +} + +static int bq27541_set_allow_reading(int enable) +{ + if (bq27541_di) + bq27541_di->allow_reading = enable; + + return 0; +} + +static int bq27541_set_lcd_off_status(int off) +{ + int soc; + + pr_info("off=%d\n", off); + if (bq27541_di) { + if (off) { + soc = bq27541_get_batt_bq_soc(); + bq27541_di->lcd_off_delt_soc = + bq27541_di->soc_pre - soc; + pr_info("lcd_off_delt_soc:%d,soc=%d,soc_pre=%d\n", + bq27541_di->lcd_off_delt_soc, soc, + bq27541_di->soc_pre); + get_current_time(&bq27541_di->lcd_off_time); + bq27541_di->lcd_is_off = true; + } else { + bq27541_di->lcd_is_off = false; + bq27541_di->lcd_off_delt_soc = 0; + } + } + return 0; +} + +static int bq27541_get_fastchg_started_status(bool fastchg_started_status) +{ + if (bq27541_di) + bq27541_di->fastchg_started = fastchg_started_status; + + return 0; +} + +static struct external_battery_gauge bq27541_batt_gauge = { + .get_battery_mvolts = bq27541_get_battery_mvolts, + .get_battery_temperature = bq27541_get_battery_temperature, + .is_battery_present = bq27541_is_battery_present, + .is_battery_temp_within_range = bq27541_is_battery_temp_within_range, + .is_battery_id_valid = bq27541_is_battery_id_valid, + .get_batt_remaining_capacity + = bq27541_get_batt_remaining_capacity, + .get_batt_health = bq27541_get_batt_health, + .get_batt_bq_soc = bq27541_get_batt_bq_soc, +#ifdef CONFIG_GAUGE_BQ27411 + .get_device_type = bq27541_get_device_type, +#endif + .get_battery_soc = bq27541_get_battery_soc, + .get_average_current = bq27541_get_average_current, + .set_allow_reading = bq27541_set_allow_reading, + .set_lcd_off_status = bq27541_set_lcd_off_status, + .fast_chg_started_status = bq27541_get_fastchg_started_status, +}; +#define BATTERY_SOC_UPDATE_MS 12000 +#define LOW_BAT_SOC_UPDATE_MS 6000 +#define RESUME_SCHDULE_SOC_UPDATE_WORK_MS 60000 + +static int is_usb_pluged(void) +{ + static struct power_supply *psy; + union power_supply_propval ret = {0,}; + int Vbus, rc; + + if (!psy) { + psy = power_supply_get_by_name("battery"); + if (!psy) { + pr_err("failed to get ps battery\n"); + return -EINVAL; + } + } + + rc = power_supply_get_property(psy, POWER_SUPPLY_PROP_CHARGE_NOW, &ret); + if (rc) { + pr_err("failed to get POWER_SUPPLY_PROP_CHARGE_NOW\n"); + return -EINVAL; + } + if (ret.intval <= 0) + return false; + + Vbus = ret.intval; + return (Vbus > 2500) ? true : false; +} + +static bool get_dash_started(void) +{ + if (bq27541_di && bq27541_di->fastchg_started) + return bq27541_di->fastchg_started; + else + return false; +} + +static void update_battery_soc_work(struct work_struct *work) +{ + int schedule_time, vbat; + + if (is_usb_pluged() || get_dash_started()) { + schedule_delayed_work( + &bq27541_di->battery_soc_work, + msecs_to_jiffies(BATTERY_SOC_UPDATE_MS)); + if (get_dash_started()) + return; + if (bq27541_di->set_smoothing) + return; + if (!bq27541_di->allow_reading) + bq27541_set_allow_reading(true); + return; + } + bq27541_set_allow_reading(true); + vbat = bq27541_get_battery_mvolts()/1000; + bq27541_get_average_current(); + bq27541_get_battery_temperature(); + bq27541_get_battery_soc(); + bq27541_get_batt_remaining_capacity(); + pr_debug("battery remain capacity:%d\n", + bq27541_get_batt_health()); + bq27541_set_allow_reading(false); + if (!bq27541_di->already_modify_smooth) + schedule_delayed_work( + &bq27541_di->modify_soc_smooth_parameter, 1000); + schedule_time = + vbat < 3600 ? LOW_BAT_SOC_UPDATE_MS : BATTERY_SOC_UPDATE_MS; + schedule_delayed_work(&bq27541_di->battery_soc_work, + msecs_to_jiffies(schedule_time)); +} + +static bool bq27541_registered; +bool get_extern_fg_regist_done(void) +{ + return bq27541_registered; +} +bool get_extern_bq_present(void) +{ + if (bq27541_di) + return bq27541_di->bq_present; + return 0; +} + +#ifdef CONFIG_GAUGE_BQ27411 +static void gauge_set_cmd_addr(int device_type) +{ + if (device_type == DEVICE_BQ27541) { + bq27541_di->cmd_addr.reg_temp = BQ27541_REG_TEMP; + bq27541_di->cmd_addr.reg_volt = BQ27541_REG_VOLT; + bq27541_di->cmd_addr.reg_rm = BQ27541_REG_RM; + bq27541_di->cmd_addr.reg_ai = BQ27541_REG_AI; + bq27541_di->cmd_addr.reg_soc = BQ27541_REG_SOC; + bq27541_di->cmd_addr.reg_helth = BQ27541_REG_NIC; + } else { /* device_bq27411 */ + bq27541_di->cmd_addr.reg_temp = BQ27411_REG_TEMP; + bq27541_di->cmd_addr.reg_volt = BQ27411_REG_VOLT; + bq27541_di->cmd_addr.reg_rm = BQ27411_REG_RM; + bq27541_di->cmd_addr.reg_ai = BQ27411_REG_AI; + bq27541_di->cmd_addr.reg_soc = BQ27411_REG_SOC; + bq27541_di->cmd_addr.reg_helth = BQ27411_REG_HEALTH; + } +} +#endif + +static void bq_modify_soc_smooth_parameter(struct work_struct *work) +{ + struct bq27541_device_info *di; + + di = container_of(work, struct bq27541_device_info, + modify_soc_smooth_parameter.work); + if (get_dash_started()) + return; + if (di->already_modify_smooth) + return; + bq27541_set_allow_reading(false); + di->set_smoothing = true; + bq27411_modify_soc_smooth_parameter(di, true); + di->set_smoothing = false; + bq27541_set_allow_reading(true); +} + +static bool check_bat_present(struct bq27541_device_info *di) +{ + int flags = 0, ret = 0; + + ret = bq27541_read(BQ27541_SUBCMD_CHEM_ID, &flags, 0, di); + if (ret < 0) { + pr_err("read bq27541 fail\n"); + di->bq_present = false; + return false; + } + di->bq_present = true; + return true; +} + +static void bq27541_hw_config(struct work_struct *work) +{ + int ret = 0, flags = 0, type = 0, fw_ver = 0; + struct bq27541_device_info *di; + + di = container_of(work, struct bq27541_device_info, + hw_config.work); + ret = bq27541_chip_config(di); + if (ret) { + pr_err("Failed to config Bq27541\n"); + /* Add for retry when config fail */ + di->retry_count--; + if (di->retry_count > 0) + schedule_delayed_work(&di->hw_config, HZ); + else + bq27541_registered = true; + + return; + } + external_battery_gauge_register(&bq27541_batt_gauge); + bq27541_information_register(&bq27541_batt_gauge); + bq27541_cntl_cmd(di, BQ27541_SUBCMD_CTNL_STATUS); + udelay(66); + bq27541_read(BQ27541_REG_CNTL, &flags, 0, di); + bq27541_cntl_cmd(di, BQ27541_SUBCMD_DEVCIE_TYPE); + udelay(66); + bq27541_read(BQ27541_REG_CNTL, &type, 0, di); + bq27541_cntl_cmd(di, BQ27541_SUBCMD_FW_VER); + udelay(66); + bq27541_read(BQ27541_REG_CNTL, &fw_ver, 0, di); + +#ifdef CONFIG_GAUGE_BQ27411 + if (type == DEVICE_TYPE_BQ27411) { + di->device_type = DEVICE_BQ27411; + pr_info("DEVICE_BQ27411\n"); + } else { + di->device_type = DEVICE_BQ27541; + pr_info("DEVICE_BQ27541\n"); + } + gauge_set_cmd_addr(di->device_type); + di->allow_reading = true; +#endif + + bq27541_registered = true; + pr_info("DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION is 0x%02X\n", + type, fw_ver); + pr_info("Complete bq27541 configuration 0x%02X\n", flags); + schedule_delayed_work( + &di->modify_soc_smooth_parameter, + SET_BQ_PARAM_DELAY_MS); +} + +static int bq27541_read_i2c(u8 reg, int *rt_value, int b_single, + struct bq27541_device_info *di) +{ + struct i2c_client *client = di->client; + struct i2c_msg msg[2]; + unsigned char data[2]; + int err; + + if (!client->adapter) + return -ENODEV; + mutex_lock(&battery_mutex); + + /* Write register */ + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = data; + data[0] = reg; + /* Read data */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + if (!b_single) + msg[1].len = 2; + else + msg[1].len = 1; + msg[1].buf = data; + err = i2c_transfer(client->adapter, msg, 2); + if (err >= 0) { + if (!b_single) + *rt_value = get_unaligned_le16(data); + else + *rt_value = data[0]; + mutex_unlock(&battery_mutex); + return 0; + } + mutex_unlock(&battery_mutex); + return err; +} + +#ifdef CONFIG_BQ27541_TEST_ENABLE +static int reg; +static int subcmd; +static ssize_t bq27541_read_stdcmd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + int temp = 0; + struct platform_device *client; + struct bq27541_device_info *di; + + client = to_platform_device(dev); + di = platform_get_drvdata(client); + + if (reg <= BQ27541_REG_ICR && reg > 0x00) { + ret = bq27541_read(reg, &temp, 0, di); + if (ret) + ret = snprintf(buf, PAGE_SIZE, "Read Error!\n"); + else + ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp); + } else + ret = snprintf(buf, PAGE_SIZE, "Register Error!\n"); + + return ret; +} + +static ssize_t bq27541_write_stdcmd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + int cmd; + int rc; + + rc = kstrtou32(buf, 0, &cmd); + if (rc != 1) + pr_err("%s,scanf error\n"); + reg = cmd; + return ret; +} + +static ssize_t bq27541_read_subcmd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + int temp = 0; + struct platform_device *client; + struct bq27541_device_info *di; + + client = to_platform_device(dev); + di = platform_get_drvdata(client); + + if (subcmd == BQ27541_SUBCMD_DEVCIE_TYPE || + subcmd == BQ27541_SUBCMD_FW_VER || + subcmd == BQ27541_SUBCMD_HW_VER || + subcmd == BQ27541_SUBCMD_CHEM_ID) { + + bq27541_cntl_cmd(di, subcmd); /* Retrieve Chip status */ + udelay(66); + ret = bq27541_read(BQ27541_REG_CNTL, &temp, 0, di); + + if (ret) + ret = snprintf(buf, PAGE_SIZE, "Read Error!\n"); + else + ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp); + } else + ret = snprintf(buf, PAGE_SIZE, "Register Error!\n"); + + return ret; +} + +static ssize_t bq27541_write_subcmd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + ssize_t ret = strnlen(buf, PAGE_SIZE); + int cmd, rc; + + rc = kstrtou32(buf, 0, &cmd); + if (rc != 1) + pr_err("%s,scanf error\n"); + subcmd = cmd; + return ret; +} + +static DEVICE_ATTR(std_cmd, 0644, bq27541_read_stdcmd, + bq27541_write_stdcmd); +static DEVICE_ATTR(sub_cmd, 0644, bq27541_read_subcmd, + bq27541_write_subcmd); +static struct attribute *fs_attrs[] = { + &dev_attr_std_cmd.attr, + &dev_attr_sub_cmd.attr, + NULL, +}; +static struct attribute_group fs_attr_group = { + .attrs = fs_attrs, +}; + +static struct platform_device this_device = { + .name = "bq27541-test", + .id = -1, + .dev.platform_data = NULL, +}; +#endif + +static void update_pre_capacity_func(struct work_struct *w) +{ + pr_info("enter\n"); + bq27541_set_allow_reading(true); + bq27541_get_battery_temperature(); + bq27541_battery_soc(bq27541_di, update_pre_capacity_data.suspend_time); + bq27541_set_allow_reading(false); + wake_unlock(&bq27541_di->update_soc_wake_lock); + pr_info("exit\n"); +} + +#define MAX_RETRY_COUNT 5 +#define DEFAULT_INVALID_SOC_PRE -22 + +static void bq27541_parse_dt(struct bq27541_device_info *di) +{ + struct device_node *node = di->dev->of_node; + + di->modify_soc_smooth = of_property_read_bool(node, + "qcom,modify-soc-smooth"); + pr_err("di->modify_soc_smooth=%d\n", di->modify_soc_smooth); +} +static int sealed(void) +{ + /*return control_cmd_read(di, CONTROL_STATUS) & (1 << 13);*/ + int value = 0; + + bq27541_cntl_cmd(bq27541_di, CONTROL_STATUS); + /*bq27541_cntl_cmd(di,CONTROL_STATUS);*/ + usleep_range(10000, 10001); + bq27541_read_i2c(CONTROL_STATUS, &value, 0, bq27541_di); + + pr_debug(" REG_CNTL: 0x%x\n", value); + + if (bq27541_di->device_type == DEVICE_BQ27541) + return value & BIT(14); + else if (bq27541_di->device_type == DEVICE_BQ27411) + return value & BIT(13); + else + return 1; +} + +static int seal(void) +{ + int i = 0; + + if (sealed()) { + pr_err("bq27541/27411 sealed,return\n"); + return 1; + } + bq27541_cntl_cmd(bq27541_di, SEAL_SUBCMD); + usleep_range(10000, 10001); + for (i = 0; i < SEAL_POLLING_RETRY_LIMIT; i++) { + if (sealed()) + return 1; + usleep_range(10000, 10001); + } + return 0; +} + + +static int unseal(u32 key) +{ + int i = 0; + + if (!sealed()) + goto out; +re_unseal: + if (bq27541_di->device_type == DEVICE_BQ27411) { + usleep_range(10000, 10001); + /*bq27541_write(CONTROL_CMD, key & 0xFFFF, false, di);*/ + bq27541_cntl_cmd(bq27541_di, 0x8000); + usleep_range(10000, 10001); + bq27541_cntl_cmd(bq27541_di, 0x8000); + usleep_range(10000, 10001); + } + bq27541_cntl_cmd(bq27541_di, 0xffff); + usleep_range(10000, 10001); + bq27541_cntl_cmd(bq27541_di, 0xffff); + usleep_range(10000, 10001); + + while (i < SEAL_POLLING_RETRY_LIMIT) { + i++; + if (!sealed()) + break; + usleep_range(10000, 10001); + goto re_unseal; + } + +out: + pr_info("bq27541 : i=%d,bq27541_di->device_type=%d\n", + i, bq27541_di->device_type); + + if (i == SEAL_POLLING_RETRY_LIMIT) { + pr_err("bq27541 failed\n"); + return 0; + } else { + return 1; + } +} + +static int bq27541_read_i2c_onebyte(u8 cmd, u8 *returnData) +{ + if (!new_client) { + pr_err(" new_client NULL,return\n"); + return 0; + } + if (cmd == BQ27541_BQ27411_CMD_INVALID) + return 0; + + mutex_lock(&battery_mutex); + *returnData = i2c_smbus_read_byte_data(new_client, cmd); + + mutex_unlock(&battery_mutex); + /*pr_err(" cmd = 0x%x, returnData = 0x%x\r\n",cmd,*returnData) ;*/ + if (*returnData < 0) + return 1; + else + return 0; +} + +static int bq27541_i2c_txsubcmd_onebyte(u8 cmd, u8 writeData) +{ + if (!new_client) { + pr_err(" new_client NULL,return\n"); + return 0; + } + if (cmd == BQ27541_BQ27411_CMD_INVALID) + return 0; + + mutex_lock(&battery_mutex); + i2c_smbus_write_byte_data(new_client, cmd, writeData); + mutex_unlock(&battery_mutex); + return 0; +} + + +static int bq27411_write_block_data_cmd(struct bq27541_device_info *di, + int block_id, u8 reg_addr, u8 new_value) +{ + int rc = 0; + u8 old_value = 0, old_csum = 0, new_csum = 0; + /*u8 new_csum_test = 0, csum_temp = 0;*/ + + usleep_range(1000, 1001); + bq27541_i2c_txsubcmd(BQ27411_DATA_CLASS_ACCESS, block_id, di); + usleep_range(10000, 10001); + rc = bq27541_read_i2c_onebyte(reg_addr, &old_value); + if (rc) { + pr_err("%s read reg_addr = 0x%x fail\n", __func__, reg_addr); + return 1; + } + if (old_value == new_value) + return 0; + usleep_range(1000, 1001); + rc = bq27541_read_i2c_onebyte(BQ27411_CHECKSUM_ADDR, &old_csum); + if (rc) { + pr_err("%s read checksum fail\n", __func__); + return 1; + } + usleep_range(1000, 1001); + bq27541_i2c_txsubcmd_onebyte(reg_addr, new_value); + usleep_range(1000, 1001); + new_csum = (old_value + old_csum - new_value) & 0xff; + usleep_range(1000, 1001); + bq27541_i2c_txsubcmd_onebyte(BQ27411_CHECKSUM_ADDR, new_csum); + pr_err("bq27411 write blk_id = 0x%x, addr = 0x%x, old_val = 0x%x, new_val = 0x%x, old_csum = 0x%x, new_csum = 0x%x\n", + block_id, reg_addr, old_value, new_value, old_csum, new_csum); + return 0; +} + +static int bq27411_read_block_data_cmd(struct bq27541_device_info *di, + int block_id, u8 reg_addr) +{ + u8 value = 0; + + usleep_range(1000, 1001); + bq27541_i2c_txsubcmd(BQ27411_DATA_CLASS_ACCESS, block_id, di); + usleep_range(10000, 10001); + bq27541_read_i2c_onebyte(reg_addr, &value); + return value; +} + +static int bq27411_enable_config_mode( + struct bq27541_device_info *di, bool enable) +{ + int config_mode = 0, i = 0, rc = 0; + + if (enable) { /*enter config mode*/ + usleep_range(1000, 1001); + bq27541_cntl_cmd(bq27541_di, BQ27411_SUBCMD_SET_CFG); + usleep_range(1000, 1001); + for (i = 0; i < BQ27411_CONFIG_MODE_POLLING_LIMIT; i++) { + i++; + rc = bq27541_read_i2c(BQ27411_SUBCMD_CONFIG_MODE, + &config_mode, 0, di); + if (rc < 0) { + pr_err("%s i2c read error\n", __func__); + return 1; + } + if (config_mode & BIT(4)) + break; + msleep(50); + } + } else { /* exit config mode*/ + usleep_range(1000, 1001); + bq27541_cntl_cmd(bq27541_di, BQ27411_SUBCMD_EXIT_CFG); + usleep_range(1000, 1001); + for (i = 0; i < BQ27411_CONFIG_MODE_POLLING_LIMIT; i++) { + i++; + rc = bq27541_read_i2c(BQ27411_SUBCMD_CONFIG_MODE, + &config_mode, 0, di); + if (rc < 0) { + pr_err("%s i2c read error\n", __func__); + return 1; + } + if ((config_mode & BIT(4)) == 0) + break; + msleep(50); + } + } + if (i == BQ27411_CONFIG_MODE_POLLING_LIMIT) { + pr_err("%s fail config_mode = 0x%x, enable = %d\n", + __func__, config_mode, enable); + return 1; + } + pr_err("%s success i = %d, config_mode = 0x%x, enable = %d\n", + __func__, i, config_mode, enable); + return 0; +} + +static bool bq27411_check_soc_smooth_parameter( + struct bq27541_device_info *di, bool is_powerup) +{ + int value_read = 0; + u8 dead_band_val = 0, op_cfgb_val = 0, dodat_val = 0, rc = 0; + + return true; /*not check because it costs 5.5 seconds*/ + + msleep(4000); + if (sealed()) { + if (!unseal(BQ27411_UNSEAL_KEY)) + return false; + msleep(50); + } + + if (is_powerup) { + dead_band_val = BQ27411_CC_DEAD_BAND_POWERUP_VALUE; + op_cfgb_val = BQ27411_OPCONFIGB_POWERUP_VALUE; + dodat_val = BQ27411_DODATEOC_POWERUP_VALUE; + } else { /*shutdown*/ + dead_band_val = BQ27411_CC_DEAD_BAND_SHUTDOWN_VALUE; + op_cfgb_val = BQ27411_OPCONFIGB_SHUTDOWN_VALUE; + dodat_val = BQ27411_DODATEOC_SHUTDOWN_VALUE; + } + rc = bq27411_enable_config_mode(di, true); + if (rc) { + pr_err("%s enable config mode fail\n", __func__); + return false; + } + /*enable block data control*/ + rc = bq27541_i2c_txsubcmd_onebyte(BQ27411_BLOCK_DATA_CONTROL, 0x00); + if (rc) { + pr_err("%s enable block data control fail\n", __func__); + goto check_error; + } + usleep_range(5000, 5001); + + /*check cc-dead-band*/ + value_read = bq27411_read_block_data_cmd(di, + BQ27411_CC_DEAD_BAND_ID, BQ27411_CC_DEAD_BAND_ADDR); + if (value_read != dead_band_val) { + pr_err("%s cc_dead_band error, value_read = 0x%x\n", + __func__, value_read); + goto check_error; + } + + /*check opconfigB*/ + value_read = bq27411_read_block_data_cmd(di, + BQ27411_OPCONFIGB_ID, + BQ27411_OPCONFIGB_ADDR); + if (value_read != op_cfgb_val) { + pr_err("%s opconfigb error, value_read = 0x%x\n", + __func__, value_read); + goto check_error; + } + + /*check dodateoc*/ + value_read = bq27411_read_block_data_cmd(di, + BQ27411_DODATEOC_ID, BQ27411_DODATEOC_ADDR); + if (value_read != dodat_val) { + pr_err("%s dodateoc error, value_read = 0x%x\n", + __func__, value_read); + goto check_error; + } + bq27411_enable_config_mode(di, false); + return true; + +check_error: + bq27411_enable_config_mode(di, false); + return false; +} + +static int bq27411_write_soc_smooth_parameter( + struct bq27541_device_info *di, bool is_powerup) +{ + int rc = 0; + u8 dead_band_val = 0, op_cfgb_val = 0, dodat_val = 0; + + if (is_powerup) { + dead_band_val = BQ27411_CC_DEAD_BAND_POWERUP_VALUE; + op_cfgb_val = BQ27411_OPCONFIGB_POWERUP_VALUE; + dodat_val = BQ27411_DODATEOC_POWERUP_VALUE; + } else { /*shutdown*/ + dead_band_val = BQ27411_CC_DEAD_BAND_SHUTDOWN_VALUE; + op_cfgb_val = BQ27411_OPCONFIGB_SHUTDOWN_VALUE; + dodat_val = BQ27411_DODATEOC_SHUTDOWN_VALUE; + } + + /*enter config mode*/ + rc = bq27411_enable_config_mode(di, true); + if (rc) { + pr_err("%s enable config mode fail\n", __func__); + return 1; + } + /*enable block data control*/ + bq27541_i2c_txsubcmd_onebyte(BQ27411_BLOCK_DATA_CONTROL, 0x00); + + usleep_range(5000, 5001); + /*step1: update cc-dead-band*/ + rc = bq27411_write_block_data_cmd(di, BQ27411_CC_DEAD_BAND_ID, + BQ27411_CC_DEAD_BAND_ADDR, dead_band_val); + if (rc) { + pr_err("%s cc_dead_band fail\n", __func__); + goto exit_config_mode; + } + /*step2: update opconfigB*/ + rc = bq27411_write_block_data_cmd(di, BQ27411_OPCONFIGB_ID, + BQ27411_OPCONFIGB_ADDR, op_cfgb_val); + if (rc) { + pr_err("%s opconfigB fail\n", __func__); + goto exit_config_mode; + } + /*step3: update dodateoc*/ + rc = bq27411_write_block_data_cmd(di, BQ27411_DODATEOC_ID, + BQ27411_DODATEOC_ADDR, dodat_val); + if (rc) { + pr_err("%s dodateoc fail\n", __func__); + goto exit_config_mode; + } + bq27411_enable_config_mode(di, false); + return 0; + +exit_config_mode: + bq27411_enable_config_mode(di, false); + return 1; +} + +static void bq27411_modify_soc_smooth_parameter( + struct bq27541_device_info *di, bool is_powerup) +{ + int rc = 0; + bool check_result = false, tried_again = false; + + if (di->modify_soc_smooth == false + || di->device_type == DEVICE_BQ27541) { + return; + } + + pr_err("%s begin\n", __func__); + if (sealed()) { + if (!unseal(BQ27411_UNSEAL_KEY)) + return; + msleep(50); + } +write_parameter: + rc = bq27411_write_soc_smooth_parameter(di, is_powerup); + if (rc && tried_again == false) { + tried_again = true; + goto write_parameter; + } else { + check_result = + bq27411_check_soc_smooth_parameter(di, is_powerup); + if (check_result == false && tried_again == false) { + tried_again = true; + goto write_parameter; + } + } + + usleep_range(1000, 1001); + if (sealed() == 0) { + usleep_range(1000, 1001); + seal(); + } + di->already_modify_smooth = true; + pr_err("%s end\n", __func__); +} + +static int bq27541_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + char *name; + struct bq27541_device_info *di; + struct bq27541_access_methods *bus; + int num; + int retval = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + update_pre_capacity_data.workqueue = + create_workqueue("update_pre_capacity"); + INIT_DELAYED_WORK(&(update_pre_capacity_data.work), + update_pre_capacity_func); + + mutex_lock(&battery_mutex); + num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); + mutex_unlock(&battery_mutex); + if (retval < 0) + return retval; + + name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); + if (!name) { + pr_err("failed to allocate device name\n"); + retval = -ENOMEM; + goto batt_failed_1; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + retval = -ENOMEM; + goto batt_failed_2; + } + di->id = num; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + retval = -ENOMEM; + goto batt_failed_3; + } + + i2c_set_clientdata(client, di); + di->dev = &client->dev; + bus->read = &bq27541_read_i2c; + di->bus = bus; + di->client = client; + + new_client = client; + + wake_lock_init(&di->update_soc_wake_lock, + WAKE_LOCK_SUSPEND, "bq_delt_soc_wake_lock"); + di->soc_pre = DEFAULT_INVALID_SOC_PRE; + di->temp_pre = 0; +#ifndef CONFIG_GAUGE_BQ27411 + di->allow_reading = true; +#endif + /* Add for retry when config fail */ + di->retry_count = MAX_RETRY_COUNT; + atomic_set(&di->suspended, 0); + +#ifdef CONFIG_BQ27541_TEST_ENABLE + platform_set_drvdata(&this_device, di); + retval = platform_device_register(&this_device); + if (!retval) { + retval = sysfs_create_group(&this_device.dev.kobj, + &fs_attr_group); + if (retval) + goto batt_failed_4; + } else + goto batt_failed_4; +#endif + + if (retval) { + pr_err("failed to setup bq27541\n"); + goto batt_failed_4; + } + + if (retval) { + pr_err("failed to powerup bq27541\n"); + goto batt_failed_4; + } + bq27541_di = di; + bq27541_parse_dt(di); + di->t_count = 0; + di->lcd_is_off = false; + INIT_DELAYED_WORK(&di->hw_config, bq27541_hw_config); + INIT_DELAYED_WORK(&di->modify_soc_smooth_parameter, + bq_modify_soc_smooth_parameter); + INIT_DELAYED_WORK(&di->battery_soc_work, update_battery_soc_work); + schedule_delayed_work(&di->hw_config, BQ27541_INIT_DELAY); + schedule_delayed_work(&di->battery_soc_work, BATTERY_SOC_UPDATE_MS); + pr_info("probe sucdess\n"); + check_bat_present(di); + return 0; + +batt_failed_4: + kfree(bus); +batt_failed_3: + kfree(di); +batt_failed_2: + kfree(name); +batt_failed_1: + mutex_lock(&battery_mutex); + idr_remove(&battery_id, num); + mutex_unlock(&battery_mutex); + + return retval; +} + +static int bq27541_battery_remove(struct i2c_client *client) +{ + struct bq27541_device_info *di = i2c_get_clientdata(client); + + external_battery_gauge_unregister(&bq27541_batt_gauge); + bq27541_information_unregister(&bq27541_batt_gauge); + bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_DLOG); + udelay(66); + bq27541_cntl_cmd(di, BQ27541_SUBCMD_DISABLE_IT); + cancel_delayed_work_sync(&di->hw_config); + cancel_delayed_work_sync(&di->modify_soc_smooth_parameter); + kfree(di->bus); + + mutex_lock(&battery_mutex); + idr_remove(&battery_id, di->id); + mutex_unlock(&battery_mutex); + + kfree(di); + return 0; +} + + +static int bq27541_battery_suspend(struct device *dev) +{ + int ret = 0; + struct bq27541_device_info *di = dev_get_drvdata(dev); + cancel_delayed_work_sync(&di->battery_soc_work); + atomic_set(&di->suspended, 1); + ret = get_current_time(&di->rtc_suspend_time); + if (ret) { + pr_err("Failed to read RTC time\n"); + return 0; + } + return 0; +} + + +/*1 minute*/ + +#define RESUME_TIME 60 +static int bq27541_battery_resume(struct device *dev) +{ + int ret = 0; + int suspend_time; + struct bq27541_device_info *di = dev_get_drvdata(dev); + + atomic_set(&di->suspended, 0); + ret = get_current_time(&di->rtc_resume_time); + if (ret) { + pr_err("Failed to read RTC time\n"); + return 0; + } + suspend_time = di->rtc_resume_time - di->rtc_suspend_time; + pr_info("suspend_time=%d\n", suspend_time); + update_pre_capacity_data.suspend_time = suspend_time; + + if (di->rtc_resume_time - di->lcd_off_time >= TWO_POINT_FIVE_MINUTES) { + pr_err("di->rtc_resume_time - di->lcd_off_time=%ld\n", + di->rtc_resume_time - di->lcd_off_time); + wake_lock(&di->update_soc_wake_lock); + get_current_time(&di->lcd_off_time); + queue_delayed_work_on(0, + update_pre_capacity_data.workqueue, + &(update_pre_capacity_data.work), + msecs_to_jiffies(1000)); + } + schedule_delayed_work(&bq27541_di->battery_soc_work, + msecs_to_jiffies(RESUME_SCHDULE_SOC_UPDATE_WORK_MS)); + return 0; +} + + +static void bq27541_shutdown(struct i2c_client *client) +{ + struct bq27541_device_info *di = i2c_get_clientdata(client); + + if (bq27541_di) { + if (di->already_modify_smooth) + bq27411_modify_soc_smooth_parameter(bq27541_di, false); + } + + if (di->soc_pre != DEFAULT_INVALID_SOC_PRE) + backup_soc_ex(di->soc_pre); +} + +static const struct of_device_id bq27541_match[] = { + { .compatible = "ti,bq27541-battery" }, + { }, +}; + +static const struct i2c_device_id bq27541_id[] = { + { "bq27541-battery", 1 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, BQ27541_id); +static const struct dev_pm_ops bq27541_pm = { + SET_SYSTEM_SLEEP_PM_OPS(bq27541_battery_suspend, bq27541_battery_resume) +}; + +static struct i2c_driver bq27541_battery_driver = { + .driver = { + .name = "bq27541-battery", + .pm = &bq27541_pm, + }, + .probe = bq27541_battery_probe, + .remove = bq27541_battery_remove, + .shutdown = bq27541_shutdown, + .id_table = bq27541_id, +}; + +static int __init bq27541_battery_init(void) +{ + int ret; + + ret = i2c_add_driver(&bq27541_battery_driver); + if (ret) + pr_err("Unable to register BQ27541 driver\n"); + + return ret; +} +module_init(bq27541_battery_init); + +static void __exit bq27541_battery_exit(void) +{ + i2c_del_driver(&bq27541_battery_driver); +} +module_exit(bq27541_battery_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("BQ27541 battery monitor driver"); diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 076cd49e6dd5..305d9dbdee94 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -443,6 +443,7 @@ struct fg_chip { bool profile_available; bool profile_loaded; bool battery_missing; + bool use_external_fg; bool fg_restarting; bool charge_full; bool recharge_soc_adjusted; diff --git a/drivers/power/supply/qcom/oneplus_fastcg.c b/drivers/power/supply/qcom/oneplus_fastcg.c new file mode 100644 index 000000000000..7211b8c5322c --- /dev/null +++ b/drivers/power/supply/qcom/oneplus_fastcg.c @@ -0,0 +1,1359 @@ +/**************************************************** + **Description:fastchg update firmware and driver + *****************************************************/ +#define pr_fmt(fmt) "FASTCHG: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BYTE_OFFSET 2 +#define BYTES_TO_WRITE 16 +#define ERASE_COUNT 384 /* 0x8800-0x9FFF */ +#define READ_COUNT 192 +#define FW_CHECK_FAIL 0 +#define FW_CHECK_SUCCESS 1 + +#define SHOW_FW_VERSION_DELAY_MS 18000 +static struct pm_qos_request big_cpu_update_freq; + +struct fastchg_device_info { + struct i2c_client *client; + struct miscdevice dash_device; + struct mutex read_mutex; + wait_queue_head_t read_wq; + + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspended; + struct pinctrl_state *pinctrl_mcu_data_state_active; + struct pinctrl_state *pinctrl_mcu_data_state_suspended; + struct pinctrl *pinctrl; + bool fast_chg_started; + bool fast_low_temp_full; + bool fast_chg_ing; + bool fast_switch_to_normal; + bool fast_normal_to_warm; + bool irq_enabled; + bool fast_chg_allow; + bool firmware_already_updated; + int adapter_update_report; + int adapter_update_real; + int battery_type; + int irq; + int mcu_en_gpio; + int usb_sw_1_gpio; + int usb_sw_2_gpio; + int ap_clk; + int ap_data; + int dashchg_fw_ver_count; + + struct power_supply *batt_psy; + struct work_struct fastcg_work; + struct work_struct charger_present_status_work; + struct timer_list watchdog; + struct wake_lock fastchg_wake_lock; + struct wake_lock fastchg_update_fireware_lock; + + struct delayed_work update_firmware; + struct delayed_work update_fireware_version_work; + struct delayed_work adapter_update_work; + char fw_id[12]; + char manu_name[12]; +}; + +struct fastchg_device_info *fastchg_di; + +static unsigned char *dashchg_firmware_data; +static struct clk *snoc_clk, *cnoc_clk; +static struct i2c_client *mcu_client; + +void opchg_set_data_active(struct fastchg_device_info *chip) +{ + gpio_direction_input(chip->ap_data); + pinctrl_select_state(chip->pinctrl, + chip->pinctrl_mcu_data_state_active); +} + +void set_mcu_en_gpio_value(int value) +{ + if (gpio_is_valid(fastchg_di->mcu_en_gpio)) + gpio_direction_output(fastchg_di->mcu_en_gpio, value); +} + +void mcu_en_reset(void) +{ + if (gpio_is_valid(fastchg_di->mcu_en_gpio)) + gpio_direction_output(fastchg_di->mcu_en_gpio, 1); +} + +void mcu_en_gpio_set(int value) +{ + if (value) { + if (gpio_is_valid(fastchg_di->mcu_en_gpio)) + gpio_direction_output(fastchg_di->mcu_en_gpio, 0); + } else { + if (gpio_is_valid(fastchg_di->mcu_en_gpio)) { + gpio_direction_output(fastchg_di->mcu_en_gpio, 1); + usleep_range(10000, 10001); + gpio_direction_output(fastchg_di->mcu_en_gpio, 0); + } + } +} +#define ADAPTER_UPDATE_DELAY 1400 + +void usb_sw_gpio_set(int value) +{ + pr_info("set usb_sw_gpio=%d\n", value); + if (!gpio_is_valid(fastchg_di->usb_sw_1_gpio) + && !gpio_is_valid(fastchg_di->usb_sw_2_gpio)) { + pr_err("gpio is invalid\n"); + return; + } + + if (value) { + gpio_direction_output(fastchg_di->usb_sw_1_gpio, 1); + gpio_direction_output(fastchg_di->usb_sw_2_gpio, 1); + } else { + gpio_direction_output(fastchg_di->usb_sw_1_gpio, 0); + gpio_direction_output(fastchg_di->usb_sw_2_gpio, 0); + } + fastchg_di->fast_chg_allow = value; + + pr_info("get usb_sw_gpio=%d&%d\n" + , gpio_get_value(fastchg_di->usb_sw_1_gpio) + , gpio_get_value(fastchg_di->usb_sw_2_gpio)); +} + +static int set_property_on_smbcharger( + enum power_supply_property prop, bool data) +{ + static struct power_supply *psy; + union power_supply_propval value = {data, }; + int ret; + + if (!psy) { + psy = power_supply_get_by_name("battery"); + if (!psy) { + pr_err("failed to get ps battery\n"); + return -EINVAL; + } + } + ret = power_supply_set_property(psy, prop, &value); + + if (ret) + return -EINVAL; + + return 0; +} + + +static int oneplus_dash_i2c_read( + struct i2c_client *client, u8 addr, s32 len, u8 *rxbuf) +{ + return i2c_smbus_read_i2c_block_data(client, addr, len, rxbuf); +} + +static int oneplus_dash_i2c_write( + struct i2c_client *client, u8 addr, s32 len, u8 *txbuf) +{ + return i2c_smbus_write_i2c_block_data(client, addr, len, txbuf); +} + +static bool dashchg_fw_check(void) +{ + unsigned char addr_buf[2] = {0x88, 0x00}; + unsigned char data_buf[32] = {0x0}; + int rc, i, j, addr; + int fw_line = 0; + + rc = oneplus_dash_i2c_write(mcu_client, 0x01, 2, &addr_buf[0]); + if (rc < 0) { + pr_err("%s i2c_write 0x01 error\n", __func__); + goto i2c_err; + } + + usleep_range(2000, 2001); + for (i = 0; i < READ_COUNT; i++) { + oneplus_dash_i2c_read(mcu_client, 0x03, 16, &data_buf[0]); + usleep_range(2000, 2001); + oneplus_dash_i2c_read(mcu_client, 0x03, 16, &data_buf[16]); + addr = 0x8800 + i * 32; + + /* compare recv_buf with dashchg_firmware_data[] begin */ + if (addr == ((dashchg_firmware_data[fw_line * 34 + 1] << 8) + | dashchg_firmware_data[fw_line * 34])) { + for (j = 0; j < 32; j++) { + if (data_buf[j] != dashchg_firmware_data[fw_line * 34 + 2 + j]) { + pr_info("%s fail,data_buf[%d]:0x%x!=dashchg_firmware_data[%d]:0x%x\n", + __func__, j, data_buf[j], + (fw_line * 34 + 2 + j), + dashchg_firmware_data[fw_line * 34 + 2 + j]); + pr_info("%s addr = 0x%x", __func__, addr); + for (j = 0; j <= 31; j++) + pr_info("%x\n", data_buf[i]); + return FW_CHECK_FAIL; + } + } + fw_line++; + } else { + /*pr_err("%s addr dismatch,addr:0x%x,stm_data:0x%x\n",__func__,*/ + /*addr,(dashchg_firmware_data[fw_line * 34 + 1] << 8) | */ + /*dashchg_firmware_data[fw_line * 34]);*/ + } + /* compare recv_buf with dashchg_firmware_data[] end */ + } + pr_info("result=success\n"); + return FW_CHECK_SUCCESS; +i2c_err: + pr_err("result=fail\n"); + return FW_CHECK_FAIL; +} + +static int dashchg_fw_write( + unsigned char *data_buf, + unsigned int offset, unsigned int length) +{ + unsigned int count = 0; + unsigned char zero_buf[1] = {0}; + unsigned char temp_buf[1] = {0}; + unsigned char addr_buf[2] = {0x88, 0x00}; + int rc; + + count = offset; + /* write data begin */ + while (count < (offset + length)) { + addr_buf[0] = data_buf[count + 1]; + addr_buf[1] = data_buf[count]; + + rc = oneplus_dash_i2c_write(mcu_client, 0x01, 2, &addr_buf[0]); + if (rc < 0) { + pr_err("i2c_write 0x01 error\n"); + return -EFAULT; + } + + /* write 16 bytes data to dashchg */ + oneplus_dash_i2c_write(mcu_client, + 0x02, BYTES_TO_WRITE, &data_buf[count+BYTE_OFFSET]); + oneplus_dash_i2c_write(mcu_client, 0x05, 1, &zero_buf[0]); + oneplus_dash_i2c_read(mcu_client, 0x05, 1, &temp_buf[0]); + + /* write 16 bytes data to dashchg again */ + oneplus_dash_i2c_write(mcu_client, + 0x02, BYTES_TO_WRITE, + &data_buf[count+BYTE_OFFSET+BYTES_TO_WRITE]); + oneplus_dash_i2c_write(mcu_client, 0x05, 1, &zero_buf[0]); + oneplus_dash_i2c_read(mcu_client, 0x05, 1, &temp_buf[0]); + + count = count + BYTE_OFFSET + 2 * BYTES_TO_WRITE; + + usleep_range(2000, 2001); + if (count > (offset + length - 1)) + break; + } + return 0; +} + +static irqreturn_t irq_rx_handler(int irq, void *dev_id); +static void reset_mcu_and_request_irq(struct fastchg_device_info *di) +{ + int ret; + + pr_info("\n"); + gpio_direction_output(di->ap_clk, 1); + usleep_range(10000, 10001); + gpio_direction_output(di->mcu_en_gpio, 1); + usleep_range(10000, 10001); + gpio_direction_output(di->mcu_en_gpio, 0); + usleep_range(5000, 5001); + opchg_set_data_active(di); + di->irq = gpio_to_irq(di->ap_data); + + /* 0x01:rising edge, 0x02:falling edge */ + ret = request_irq(di->irq, irq_rx_handler, + IRQF_TRIGGER_RISING, "mcu_data", di); + if (ret < 0) + pr_err("request ap rx irq failed.\n"); + else + di->irq_enabled = true; +} + + +static void dashchg_fw_update(struct work_struct *work) +{ + unsigned char zero_buf[1] = {0}; + unsigned char addr_buf[2] = {0x88, 0x00}; + unsigned char temp_buf[1] = {0}; + int i, rc = 0; + unsigned int addr = 0x8800; + int download_again = 0; + struct fastchg_device_info *di = container_of(work, + struct fastchg_device_info, + update_firmware.work); + + wake_lock(&di->fastchg_update_fireware_lock); + rc = dashchg_fw_check(); + if (rc == FW_CHECK_SUCCESS) { + di->firmware_already_updated = true; + reset_mcu_and_request_irq(di); + wake_unlock(&di->fastchg_update_fireware_lock); + set_property_on_smbcharger(POWER_SUPPLY_PROP_SWITCH_DASH, true); + pr_info("FW check success\n"); + return; + } + pr_info("start erasing data.......\n"); + +update_fw: + /* erase address 0x200-0x7FF */ + for (i = 0; i < ERASE_COUNT; i++) { + /* first:set address */ + rc = oneplus_dash_i2c_write(mcu_client, 0x01, 2, &addr_buf[0]); + if (rc < 0) { + pr_err("dashchg_update_fw, i2c_write 0x01 error\n"); + goto update_fw_err; + } + + /* erase data:0x10 words once */ + oneplus_dash_i2c_write(mcu_client, 0x04, 1, &zero_buf[0]); + usleep_range(1000, 1001); + oneplus_dash_i2c_read(mcu_client, 0x04, 1, &temp_buf[0]); + + /* erase data:0x10 words once */ + addr = addr + 0x10; + addr_buf[0] = addr >> 8; + addr_buf[1] = addr & 0xFF; + } + usleep_range(10000, 10001); + dashchg_fw_write(dashchg_firmware_data, 0, di->dashchg_fw_ver_count); + + /* fw check begin:read data from mcu and compare*/ + /*it with dashchg_firmware_data[] */ + rc = dashchg_fw_check(); + if (rc == FW_CHECK_FAIL) { + download_again++; + if (download_again > 3) + goto update_fw_err; + mcu_en_gpio_set(0); + msleep(1000); + pr_err("fw check fail, download fw again\n"); + goto update_fw; + } + /* fw check end */ + + usleep_range(2000, 2001); + /* jump to app code begin */ + oneplus_dash_i2c_write(mcu_client, 0x06, 1, &zero_buf[0]); + oneplus_dash_i2c_read(mcu_client, 0x06, 1, &temp_buf[0]); + /* jump to app code end */ + di->firmware_already_updated = true; + reset_mcu_and_request_irq(di); + wake_unlock(&di->fastchg_update_fireware_lock); + set_property_on_smbcharger(POWER_SUPPLY_PROP_SWITCH_DASH, true); + pr_info("result=success\n"); + return; + +update_fw_err: + di->firmware_already_updated = true; + reset_mcu_and_request_irq(di); + wake_unlock(&di->fastchg_update_fireware_lock); + set_property_on_smbcharger(POWER_SUPPLY_PROP_SWITCH_DASH, true); + pr_err("result=fail\n"); +} + + +static struct external_battery_gauge *bq27541_data; +void bq27541_information_register( + struct external_battery_gauge *fast_chg) +{ + if (bq27541_data) { + bq27541_data = fast_chg; + pr_err("multiple battery gauge called\n"); + } else { + bq27541_data = fast_chg; + } +} +EXPORT_SYMBOL(bq27541_information_register); + +void bq27541_information_unregister(struct external_battery_gauge *batt_gauge) +{ + bq27541_data = NULL; +} + +static bool bq27541_fast_chg_started(void) +{ + if (fastchg_di) + return fastchg_di->fast_chg_started; + + return false; +} + +static bool bq27541_get_fast_low_temp_full(void) +{ + if (fastchg_di) + return fastchg_di->fast_low_temp_full; + + return false; +} + +static int bq27541_set_fast_chg_allow(bool enable) +{ + if (fastchg_di) + fastchg_di->fast_chg_allow = enable; + + return 0; +} + +static bool bq27541_get_fast_chg_allow(void) +{ + if (fastchg_di) + return fastchg_di->fast_chg_allow; + + return false; +} + +static bool bq27541_fast_switch_to_normal(void) +{ + if (fastchg_di) + return fastchg_di->fast_switch_to_normal; + + return false; +} + +static bool bq27541_get_fast_chg_ing(void) +{ + if (fastchg_di) + return fastchg_di->fast_chg_ing; + + return false; +} + + +static int bq27541_set_switch_to_noraml_false(void) +{ + if (fastchg_di) + fastchg_di->fast_switch_to_normal = false; + + return 0; +} + +static bool get_fastchg_firmware_already_updated(void) +{ + if (fastchg_di) + return fastchg_di->firmware_already_updated; + + return false; +} + +static bool fastchg_is_usb_switch_on(void) +{ + if (fastchg_di) + return gpio_get_value(fastchg_di->usb_sw_1_gpio); + + return false; +} + +int dash_get_adapter_update_status(void) +{ + if (!fastchg_di) + return ADAPTER_FW_UPDATE_NONE; + else + return fastchg_di->adapter_update_report; +} + +static struct external_battery_gauge fastcharge_information = { + .fast_chg_started = + bq27541_fast_chg_started, + .get_fast_low_temp_full = + bq27541_get_fast_low_temp_full, + .fast_switch_to_normal = + bq27541_fast_switch_to_normal, + .get_fast_chg_ing = + bq27541_get_fast_chg_ing, + .set_fast_chg_allow = + bq27541_set_fast_chg_allow, + .get_fast_chg_allow = + bq27541_get_fast_chg_allow, + .set_switch_to_noraml_false = + bq27541_set_switch_to_noraml_false, + .get_fastchg_firmware_already_updated = + get_fastchg_firmware_already_updated, + .is_usb_switch_on = fastchg_is_usb_switch_on, + .get_adapter_update = dash_get_adapter_update_status, +}; + +static struct notify_dash_event *notify_event; + +void notify_dash_unplug_register(struct notify_dash_event *event) +{ + if (notify_event) { + notify_event = event; + pr_err("multiple battery gauge called\n"); + } else { + notify_event = event; + } +} +EXPORT_SYMBOL(notify_dash_unplug_register); + +void notify_dash_unplug_unregister(struct notify_dash_event *notify_event) +{ + notify_event = NULL; +} +EXPORT_SYMBOL(notify_dash_unplug_unregister); + +static void mcu_init(struct fastchg_device_info *di) +{ + gpio_direction_output(di->ap_clk, 0); + usleep_range(1000, 1001); + gpio_direction_output(di->mcu_en_gpio, 1); + usleep_range(1000, 1001); + gpio_direction_output(di->mcu_en_gpio, 0); +} + +static irqreturn_t irq_rx_handler(int irq, void *dev_id) +{ + struct fastchg_device_info *di = dev_id; + + pr_debug("triggered\n"); + schedule_work(&di->fastcg_work); + return IRQ_HANDLED; +} + +static void oneplus_notify_dash_charger_present(bool status) +{ + if (notify_event && notify_event->notify_dash_charger_present) + notify_event->notify_dash_charger_present(status); +} + +static void oneplus_notify_pmic_check_charger_present(void) +{ + if (notify_event && notify_event->notify_event) + notify_event->notify_event(); +} + +static void notify_check_usb_suspend(bool status, bool check_power_ok) +{ + if (notify_event && notify_event->op_contrl) + notify_event->op_contrl(status, check_power_ok); +} + +static void update_charger_present_status(struct work_struct *work) +{ + notify_check_usb_suspend(true, true); + oneplus_notify_dash_charger_present(false); + oneplus_notify_pmic_check_charger_present(); +} + +static int op_get_device_type(void) +{ + if (bq27541_data && bq27541_data->get_device_type) + return bq27541_data->get_device_type(); + else + return 0; +} + +static int onplus_get_battery_mvolts(void) +{ + if (bq27541_data && bq27541_data->get_battery_mvolts) + return bq27541_data->get_battery_mvolts(); + else + return 4010 * 1000; /* retrun 4.01v for default */ +} + +static int onplus_get_battery_temperature(void) +{ + if (bq27541_data && bq27541_data->get_battery_temperature) + return bq27541_data->get_battery_temperature(); + else + return 255; /* retrun 25.5 for default temp */ +} + +static int onplus_get_batt_remaining_capacity(void) +{ + if (bq27541_data && bq27541_data->get_batt_remaining_capacity) + return bq27541_data->get_batt_remaining_capacity(); + else + return 5; /* retrun 5 for default remaining_capacity */ +} + +static int onplus_get_battery_soc(void) +{ + if (bq27541_data && bq27541_data->get_battery_soc) + return bq27541_data->get_battery_soc(); + else + return 50; /* retrun 50 for default soc */ +} + +static int onplus_get_average_current(void) +{ + if (bq27541_data && bq27541_data->get_average_current) + return bq27541_data->get_average_current(); + else + return 666 * 1000; /* retrun 666ma for default current */ +} + +void switch_mode_to_normal(void) +{ + usb_sw_gpio_set(0); + mcu_en_gpio_set(1); +} + +static void update_fast_chg_started(void) +{ + if (bq27541_data && bq27541_data->fast_chg_started_status) + bq27541_data->fast_chg_started_status( + fastchg_di->fast_chg_started); +} + +static void request_mcu_irq(struct fastchg_device_info *di) +{ + int retval; + + opchg_set_data_active(di); + gpio_set_value(di->ap_clk, 0); + usleep_range(10000, 10001); + gpio_set_value(di->ap_clk, 1); + if (di->adapter_update_real + != ADAPTER_FW_NEED_UPDATE) { + pr_info("%s\n", __func__); + if (!di->irq_enabled) { + retval = request_irq(di->irq, irq_rx_handler, + IRQF_TRIGGER_RISING, "mcu_data", di); + if (retval < 0) + pr_err("request ap rx irq failed.\n"); + else + di->irq_enabled = true; + } + } else { + di->irq_enabled = true; +} +} +static void fastcg_work_func(struct work_struct *work) +{ + struct fastchg_device_info *di = container_of(work, + struct fastchg_device_info, + fastcg_work); + pr_info("\n"); + if (di->irq_enabled) { + free_irq(di->irq, di); + msleep(25); + di->irq_enabled = false; + wake_up(&di->read_wq); + } +} + +static void update_fireware_version_func(struct work_struct *work) +{ + struct fastchg_device_info *di = container_of(work, + struct fastchg_device_info, + update_fireware_version_work.work); + + if (!dashchg_firmware_data || di->dashchg_fw_ver_count == 0) + return; + + snprintf(di->fw_id, 255, "0x%x", + dashchg_firmware_data[di->dashchg_fw_ver_count - 4]); + snprintf(di->manu_name, 255, "%s", "ONEPLUS"); + push_component_info(FAST_CHARGE, di->fw_id, di->manu_name); +} +void di_watchdog(unsigned long data) +{ + struct fastchg_device_info *di = (struct fastchg_device_info *)data; + + pr_err("di_watchdog can't receive mcu data\n"); + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + di->fast_switch_to_normal = false; + di->fast_low_temp_full = false; + di->fast_chg_allow = false; + di->fast_normal_to_warm = false; + di->fast_chg_ing = false; + + /* switch off fast chg */ + switch_mode_to_normal(); + schedule_work(&di->charger_present_status_work); + pr_err("switch off fastchg\n"); + + wake_unlock(&di->fastchg_wake_lock); +} + +#define MAX_BUFFER_SIZE 1024 +#define ALLOW_DATA 0x2 +#define REJECT_DATA 0x11 +static void dash_write(struct fastchg_device_info *di, int data) +{ + int i; + int device_type = op_get_device_type(); + + usleep_range(2000, 2001); + gpio_direction_output(di->ap_data, 0); + pinctrl_select_state(di->pinctrl, + di->pinctrl_mcu_data_state_suspended); + for (i = 0; i < 3; i++) { + if (i == 0) + gpio_set_value(di->ap_data, data >> 1); + else if (i == 1) + gpio_set_value(di->ap_data, data & 0x1); + else + gpio_set_value(di->ap_data, device_type); + gpio_set_value(di->ap_clk, 0); + usleep_range(1000, 1001); + gpio_set_value(di->ap_clk, 1); + usleep_range(19000, 19001); + } +} + +static int dash_read(struct fastchg_device_info *di) +{ + int i; + int bit = 0; + int data = 0; + + for (i = 0; i < 7; i++) { + gpio_set_value(di->ap_clk, 0); + usleep_range(1000, 1001); + gpio_set_value(di->ap_clk, 1); + usleep_range(19000, 19001); + bit = gpio_get_value(di->ap_data); + data |= bit<<(6-i); + } + pr_err("recv data:0x%x\n", data); + return data; +} + +static int dash_dev_open(struct inode *inode, struct file *filp) +{ + struct fastchg_device_info *dash_dev = container_of(filp->private_data, + struct fastchg_device_info, dash_device); + + filp->private_data = dash_dev; + pr_debug("%d,%d\n", imajor(inode), iminor(inode)); + return 0; +} + +static ssize_t dash_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct fastchg_device_info *di = filp->private_data; + + int data; + int ret = 0; + + mutex_lock(&di->read_mutex); + while (1) { + ret = wait_event_interruptible(di->read_wq, + (!di->irq_enabled)); + if (ret) + goto fail; + if (di->irq_enabled) + pr_err("dash false wakeup,ret=%d\n", ret); + data = dash_read(di); + mutex_unlock(&di->read_mutex); + if (copy_to_user(buf, &data, 1)) { + pr_err("failed to copy to user space\n"); + return -EFAULT; + } + break; + } + return ret; +fail: + mutex_unlock(&di->read_mutex); + return ret; +} +static struct op_adapter_chip *g_adapter_chip; + +static void adapter_update_work_func(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct fastchg_device_info *chip = + container_of(dwork, + struct fastchg_device_info, adapter_update_work); + bool update_result = false; + int i = 0; + + if (!g_adapter_chip) { + pr_info("%s g_adapter_chip NULL\n", __func__); + return; + } + pr_info("%s begin\n", __func__); + opchg_set_data_active(chip); + if (snoc_clk) { + clk_set_rate(snoc_clk, 200000000); + clk_prepare_enable(snoc_clk); + } + if (cnoc_clk) { + clk_set_rate(cnoc_clk, 75000000); + clk_prepare_enable(cnoc_clk); + } + pm_qos_update_request(&big_cpu_update_freq, MAX_CPUFREQ); + msleep(1000); + for (i = 0; i < 3; i++) { + update_result = + g_adapter_chip->vops->adapter_update(g_adapter_chip, + chip->ap_clk, chip->ap_data); + if (update_result == true) + break; + if (i < 1) + msleep(1650); + } + msleep(5000); + if (update_result) { + chip->adapter_update_real = ADAPTER_FW_UPDATE_SUCCESS; + } else { + chip->adapter_update_real = ADAPTER_FW_UPDATE_FAIL; + chip->adapter_update_report = chip->adapter_update_real; + } + msleep(20); + mcu_en_gpio_set(1); + chip->fast_chg_started = false; + chip->fast_chg_allow = false; + chip->fast_chg_ing = false; + msleep(1000); + if (update_result) { + msleep(2000); + chip->adapter_update_report = ADAPTER_FW_UPDATE_SUCCESS; + } + notify_check_usb_suspend(true, false); + oneplus_notify_pmic_check_charger_present(); + oneplus_notify_dash_charger_present(false); + reset_mcu_and_request_irq(chip); + pm_qos_update_request(&big_cpu_update_freq, MIN_CPUFREQ); + clk_disable_unprepare(snoc_clk); + clk_disable_unprepare(cnoc_clk); + + pr_info("%s end update_result:%d\n", + __func__, update_result); + wake_unlock(&chip->fastchg_wake_lock); + +} + +static void dash_adapter_update(struct fastchg_device_info *chip) +{ + pr_err("%s\n", __func__); + /*schedule_delayed_work_on(5,*/ + /*&chip->adapter_update_work,*/ + /*round_jiffies_relative(*/ + /*msecs_to_jiffies(ADAPTER_UPDATE_DELAY)));*/ + schedule_delayed_work(&chip->adapter_update_work, + msecs_to_jiffies(ADAPTER_UPDATE_DELAY)); +} +void op_adapter_init(struct op_adapter_chip *chip) +{ + g_adapter_chip = chip; +} + +#define DASH_IOC_MAGIC 0xff +#define DASH_NOTIFY_FIRMWARE_UPDATE _IO(DASH_IOC_MAGIC, 1) +#define DASH_NOTIFY_FAST_PRESENT _IOW(DASH_IOC_MAGIC, 2, int) +#define DASH_NOTIFY_FAST_ABSENT _IOW(DASH_IOC_MAGIC, 3, int) +#define DASH_NOTIFY_NORMAL_TEMP_FULL _IOW(DASH_IOC_MAGIC, 4, int) +#define DASH_NOTIFY_LOW_TEMP_FULL _IOW(DASH_IOC_MAGIC, 5, int) +#define DASH_NOTIFY_BAD_CONNECTED _IOW(DASH_IOC_MAGIC, 6, int) +#define DASH_NOTIFY_TEMP_OVER _IOW(DASH_IOC_MAGIC, 7, int) +#define DASH_NOTIFY_ADAPTER_FW_UPDATE _IOW(DASH_IOC_MAGIC, 8, int) +#define DASH_NOTIFY_BTB_TEMP_OVER _IOW(DASH_IOC_MAGIC, 9, int) +#define DASH_NOTIFY_ALLOW_READING_IIC _IOW(DASH_IOC_MAGIC, 10, int) +#define DASH_NOTIFY_UNDEFINED_CMD _IO(DASH_IOC_MAGIC, 11) +#define DASH_NOTIFY_INVALID_DATA_CMD _IO(DASH_IOC_MAGIC, 12) +#define DASH_NOTIFY_REQUEST_IRQ _IO(DASH_IOC_MAGIC, 13) +#define DASH_NOTIFY_UPDATE_DASH_PRESENT _IOW(DASH_IOC_MAGIC, 14, int) + +static long dash_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct fastchg_device_info *di = filp->private_data; + int volt = 0; + int temp = 0; + int soc = 0; + int current_now = 0; + int remain_cap = 0; + + switch (cmd) { + case DASH_NOTIFY_FIRMWARE_UPDATE: + schedule_delayed_work(&di->update_firmware, + msecs_to_jiffies(2200)); + break; + case DASH_NOTIFY_FAST_PRESENT: + oneplus_notify_dash_charger_present(true); + if (arg == DASH_NOTIFY_FAST_PRESENT + 1) { + wake_lock(&di->fastchg_wake_lock); + bq27541_data->set_allow_reading(false); + di->fast_chg_allow = false; + di->fast_normal_to_warm = false; + mod_timer(&di->watchdog, + jiffies + msecs_to_jiffies(15000)); + } else if (arg == DASH_NOTIFY_FAST_PRESENT + 2) { + pr_err("REJECT_DATA\n"); + dash_write(di, REJECT_DATA); + } else if (arg == DASH_NOTIFY_FAST_PRESENT + 3) { + notify_check_usb_suspend(false, false); + dash_write(di, ALLOW_DATA); + di->fast_chg_started = true; + } + break; + case DASH_NOTIFY_FAST_ABSENT: + if (arg == DASH_NOTIFY_FAST_ABSENT + 1) { + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + di->fast_chg_allow = false; + di->fast_switch_to_normal = false; + di->fast_normal_to_warm = false; + di->fast_chg_ing = false; + pr_err("fastchg stop unexpectly, switch off fastchg\n"); + switch_mode_to_normal(); + del_timer(&di->watchdog); + dash_write(di, REJECT_DATA); + } else if (arg == DASH_NOTIFY_FAST_ABSENT + 2) { + notify_check_usb_suspend(true, true); + oneplus_notify_dash_charger_present(false); + oneplus_notify_pmic_check_charger_present(); + wake_unlock(&di->fastchg_wake_lock); + } + break; + case DASH_NOTIFY_ALLOW_READING_IIC: + if (arg == DASH_NOTIFY_ALLOW_READING_IIC + 1) { + bq27541_data->set_allow_reading(true); + di->fast_chg_started = true; + di->fast_chg_ing = true; + volt = onplus_get_battery_mvolts(); + temp = onplus_get_battery_temperature(); + remain_cap = + onplus_get_batt_remaining_capacity(); + soc = onplus_get_battery_soc(); + current_now = onplus_get_average_current(); + pr_err("volt:%d,temp:%d,remain_cap:%d,soc:%d,current:%d\n", + volt, temp, remain_cap, soc, current_now); + if (!di->batt_psy) + di->batt_psy = + power_supply_get_by_name("battery"); + if (di->batt_psy) + power_supply_changed(di->batt_psy); + bq27541_data->set_allow_reading(false); + mod_timer(&di->watchdog, + jiffies + msecs_to_jiffies(15000)); + dash_write(di, ALLOW_DATA); + } + break; + case DASH_NOTIFY_BTB_TEMP_OVER: + mod_timer(&di->watchdog, + jiffies + msecs_to_jiffies(15000)); + dash_write(di, ALLOW_DATA); + break; + case DASH_NOTIFY_BAD_CONNECTED: + case DASH_NOTIFY_NORMAL_TEMP_FULL: + if (arg == DASH_NOTIFY_NORMAL_TEMP_FULL + 1) { + pr_err("fastchg full, switch off fastchg, set usb_sw_gpio 0\n"); + switch_mode_to_normal(); + del_timer(&di->watchdog); + } else if (arg == DASH_NOTIFY_NORMAL_TEMP_FULL + 2) { + di->fast_switch_to_normal = true; + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + di->fast_chg_allow = false; + di->fast_chg_ing = false; + notify_check_usb_suspend(true, false); + oneplus_notify_pmic_check_charger_present(); + wake_unlock(&di->fastchg_wake_lock); + } + break; + case DASH_NOTIFY_TEMP_OVER: + if (arg == DASH_NOTIFY_TEMP_OVER + 1) { + pr_err("fastchg temp over\n"); + switch_mode_to_normal(); + del_timer(&di->watchdog); + } else if (arg == DASH_NOTIFY_TEMP_OVER + 2) { + di->fast_normal_to_warm = true; + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + di->fast_chg_allow = false; + di->fast_chg_ing = false; + notify_check_usb_suspend(true, false); + oneplus_notify_pmic_check_charger_present(); + oneplus_notify_dash_charger_present(false); + wake_unlock(&di->fastchg_wake_lock); + } + break; + case DASH_NOTIFY_ADAPTER_FW_UPDATE: + if (arg == DASH_NOTIFY_ADAPTER_FW_UPDATE + 1) { + di->adapter_update_real + = ADAPTER_FW_NEED_UPDATE; + di->adapter_update_report + = di->adapter_update_real; + } else if (arg == DASH_NOTIFY_ADAPTER_FW_UPDATE + 2) { + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + oneplus_notify_dash_charger_present(true); + dash_write(di, ALLOW_DATA); + wake_lock(&di->fastchg_wake_lock); + dash_adapter_update(di); + } + break; + case DASH_NOTIFY_UNDEFINED_CMD: + if (di->fast_chg_started) { + pr_err("UNDEFINED_CMD, switch off fastchg\n"); + switch_mode_to_normal(); + msleep(500); /* avoid i2c conflict */ + /* data err */ + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + wake_unlock(&di->fastchg_wake_lock); + di->fast_chg_allow = false; + di->fast_switch_to_normal = false; + di->fast_normal_to_warm = false; + di->fast_chg_ing = false; + notify_check_usb_suspend(true, false); + } + break; + case DASH_NOTIFY_INVALID_DATA_CMD: + if (di->fast_chg_started) { + bq27541_data->set_allow_reading(true); + di->fast_chg_started = false; + di->fast_chg_allow = false; + di->fast_switch_to_normal = false; + di->fast_normal_to_warm = false; + di->fast_chg_ing = false; + pr_err("DASH_NOTIFY_INVALID_DATA_CMD, switch off fastchg\n"); + switch_mode_to_normal(); + del_timer(&di->watchdog); + wake_unlock(&di->fastchg_wake_lock); + notify_check_usb_suspend(true, true); + oneplus_notify_pmic_check_charger_present(); + } + break; + case DASH_NOTIFY_REQUEST_IRQ: + request_mcu_irq(di); + break; + case DASH_NOTIFY_UPDATE_DASH_PRESENT: + if (arg == DASH_NOTIFY_UPDATE_DASH_PRESENT+1) + update_fast_chg_started(); + break; + default: + pr_err("bad ioctl %u\n", cmd); + } + return 0; +} + +static ssize_t dash_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct fastchg_device_info *di = filp->private_data; + + dashchg_firmware_data = kmalloc(count, GFP_ATOMIC); + /*malloc for firmware, do not free*/ + if (di->firmware_already_updated) + return 0; + di->dashchg_fw_ver_count = count; + if (copy_from_user(dashchg_firmware_data, buf, count)) { + pr_err("failed to copy from user space\n"); + kfree(dashchg_firmware_data); + return -EFAULT; + } + pr_info("fw_ver_count=%d\n", di->dashchg_fw_ver_count); + return count; +} + +static const struct file_operations dash_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = dash_dev_write, + .read = dash_dev_read, + .open = dash_dev_open, + .unlocked_ioctl = dash_dev_ioctl, +}; + +static int dash_parse_dt(struct fastchg_device_info *di) +{ + u32 flags; + struct device_node *dev_node = di->client->dev.of_node; + + if (!dev_node) { + pr_err("device tree info. missing\n"); + return -EINVAL; + } + + di->usb_sw_1_gpio = of_get_named_gpio_flags(dev_node, + "microchip,usb-sw-1-gpio", 0, &flags); + di->usb_sw_2_gpio = of_get_named_gpio_flags(dev_node, + "microchip,usb-sw-2-gpio", 0, &flags); + di->ap_clk = of_get_named_gpio_flags(dev_node, + "microchip,ap-clk", 0, &flags); + di->ap_data = of_get_named_gpio_flags(dev_node, + "microchip,ap-data", 0, &flags); + di->mcu_en_gpio = of_get_named_gpio_flags(dev_node, + "microchip,mcu-en-gpio", 0, &flags); + return 0; +} + +static int request_dash_gpios(struct fastchg_device_info *di) +{ + int ret; + + if (gpio_is_valid(di->usb_sw_1_gpio) + && gpio_is_valid(di->usb_sw_2_gpio)) { + ret = gpio_request(di->usb_sw_1_gpio, "usb_sw_1_gpio"); + if (ret) + pr_err("gpio_request failed for %d ret=%d\n", + di->usb_sw_1_gpio, ret); + else + gpio_direction_output(di->usb_sw_1_gpio, 0); + + ret = gpio_request(di->usb_sw_2_gpio, "usb_sw_2_gpio"); + if (ret) + pr_err("gpio_request failed for %d ret=%d\n", + di->usb_sw_2_gpio, ret); + else + gpio_direction_output(di->usb_sw_2_gpio, 0); + + } + + if (gpio_is_valid(di->ap_clk)) { + ret = gpio_request(di->ap_clk, "ap_clk"); + if (ret) + pr_err("gpio_request failed for %d ret=%d\n", + di->ap_clk, ret); + } + + if (gpio_is_valid(di->mcu_en_gpio)) { + ret = gpio_request(di->mcu_en_gpio, "mcu_en_gpio"); + if (ret) + pr_err("gpio_request failed for %d ret=%d\n", + di->mcu_en_gpio, ret); + else + gpio_direction_output(di->mcu_en_gpio, 0); + } + + if (gpio_is_valid(di->ap_data)) { + ret = gpio_request(di->ap_data, "mcu_data"); + if (ret) + pr_err("gpio_request failed for %d ret=%d\n", + di->ap_data, ret); + + } + + return 0; +} + +static int dash_pinctrl_init(struct fastchg_device_info *di) +{ + di->pinctrl = devm_pinctrl_get(&di->client->dev); + if (IS_ERR_OR_NULL(di->pinctrl)) { + dev_err(&di->client->dev, + "Unable to acquire pinctrl\n"); + di->pinctrl = NULL; + return 0; + } + di->pinctrl_state_active = + pinctrl_lookup_state(di->pinctrl, "mux_fastchg_active"); + if (IS_ERR_OR_NULL(di->pinctrl_state_active)) { + dev_err(&di->client->dev, + "Can not fastchg_active state\n"); + devm_pinctrl_put(di->pinctrl); + di->pinctrl = NULL; + return PTR_ERR(di->pinctrl_state_active); + } + di->pinctrl_state_suspended = + pinctrl_lookup_state(di->pinctrl, + "mux_fastchg_suspend"); + if (IS_ERR_OR_NULL(di->pinctrl_state_suspended)) { + dev_err(&di->client->dev, + "Can not fastchg_suspend state\n"); + devm_pinctrl_put(di->pinctrl); + di->pinctrl = NULL; + return PTR_ERR(di->pinctrl_state_suspended); + } + + di->pinctrl_mcu_data_state_active = + pinctrl_lookup_state(di->pinctrl, + "mcu_data_active"); + if (IS_ERR_OR_NULL(di->pinctrl_mcu_data_state_active)) { + dev_err(&di->client->dev, + "Can not mcu_data_active state\n"); + devm_pinctrl_put(di->pinctrl); + di->pinctrl = NULL; + return PTR_ERR(di->pinctrl_mcu_data_state_active); + } + di->pinctrl_mcu_data_state_suspended = + pinctrl_lookup_state(di->pinctrl, + "mcu_data_suspend"); + if (IS_ERR_OR_NULL(di->pinctrl_mcu_data_state_suspended)) { + dev_err(&di->client->dev, + "Can not fastchg_suspend state\n"); + devm_pinctrl_put(di->pinctrl); + di->pinctrl = NULL; + return PTR_ERR(di->pinctrl_mcu_data_state_suspended); + } + + if (pinctrl_select_state(di->pinctrl, + di->pinctrl_state_active) < 0) + pr_err("pinctrl set active fail\n"); + + if (pinctrl_select_state(di->pinctrl, + di->pinctrl_mcu_data_state_active) < 0) + pr_err("pinctrl set pinctrl_mcu_data_state_active fail\n"); + + return 0; + +} + +static int dash_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct fastchg_device_info *di; + int ret; + + pr_info("dash_probe\n"); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_func error\n"); + goto err_check_functionality_failed; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + ret = -ENOMEM; + goto err_check_functionality_failed; + } + + di->client = mcu_client = client; + di->firmware_already_updated = false; + di->irq_enabled = true; + di->fast_chg_ing = false; + di->fast_low_temp_full = false; + di->fast_chg_started = false; + + fastchg_di = di; + + ret = dash_parse_dt(di); + if (ret == -EINVAL) + goto err_read_dt; + + request_dash_gpios(di); + dash_pinctrl_init(di); + mutex_init(&di->read_mutex); + + init_waitqueue_head(&di->read_wq); + wake_lock_init(&di->fastchg_wake_lock, + WAKE_LOCK_SUSPEND, "fastcg_wake_lock"); + wake_lock_init(&di->fastchg_update_fireware_lock, + WAKE_LOCK_SUSPEND, "fastchg_fireware_lock"); + + INIT_WORK(&di->fastcg_work, fastcg_work_func); + INIT_WORK(&di->charger_present_status_work, + update_charger_present_status); + INIT_DELAYED_WORK(&di->update_fireware_version_work, + update_fireware_version_func); + INIT_DELAYED_WORK(&di->update_firmware, dashchg_fw_update); + INIT_DELAYED_WORK(&di->adapter_update_work, adapter_update_work_func); + + pm_qos_add_request(&big_cpu_update_freq, + PM_QOS_C1_CPUFREQ_MIN, MIN_CPUFREQ); + + init_timer(&di->watchdog); + di->watchdog.data = (unsigned long)di; + di->watchdog.function = di_watchdog; + + di->dash_device.minor = MISC_DYNAMIC_MINOR; + di->dash_device.name = "dash"; + di->dash_device.fops = &dash_dev_fops; + ret = misc_register(&di->dash_device); + /*when everything is ok, regist /dev/dash*/ + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register_failed; + } + + mcu_init(di); + + fastcharge_information_register(&fastcharge_information); + schedule_delayed_work(&di->update_fireware_version_work, + msecs_to_jiffies(SHOW_FW_VERSION_DELAY_MS)); + snoc_clk = clk_get(&client->dev, "snoc"); + cnoc_clk = clk_get(&client->dev, "cnoc"); + pr_info("dash_probe success\n"); + + return 0; + +err_misc_register_failed: + pm_qos_remove_request(&big_cpu_update_freq); +err_read_dt: + kfree(di); +err_check_functionality_failed: + pr_err("dash_probe fail\n"); + return 0; +} + +static int dash_remove(struct i2c_client *client) +{ + struct fastchg_device_info *di = dev_get_drvdata(&client->dev); + + fastcharge_information_unregister(&fastcharge_information); + if (gpio_is_valid(di->mcu_en_gpio)) + gpio_free(di->mcu_en_gpio); + if (gpio_is_valid(di->usb_sw_1_gpio)) + gpio_free(di->usb_sw_1_gpio); + if (gpio_is_valid(di->usb_sw_2_gpio)) + gpio_free(di->usb_sw_2_gpio); + if (gpio_is_valid(di->ap_clk)) + gpio_free(di->ap_clk); + if (gpio_is_valid(di->ap_data)) + gpio_free(di->ap_data); + pm_qos_remove_request(&big_cpu_update_freq); + + return 0; +} + +static void dash_shutdown(struct i2c_client *client) +{ + usb_sw_gpio_set(0); + mcu_en_reset(); +} + +static const struct of_device_id dash_match[] = { + { .compatible = "microchip,oneplus_fastcg" }, + { }, +}; + +static const struct i2c_device_id dash_id[] = { + { "dash_fastcg", 1 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, dash_id); + +static struct i2c_driver dash_fastcg_driver = { + .driver = { + .name = "dash_fastcg", + .owner = THIS_MODULE, + .of_match_table = dash_match, + }, + .probe = dash_probe, + .remove = dash_remove, + .shutdown = dash_shutdown, + .id_table = dash_id, +}; + +static int __init dash_fastcg_init(void) +{ + return i2c_add_driver(&dash_fastcg_driver); +} +module_init(dash_fastcg_init); + +static void __exit dash_fastcg_exit(void) +{ + i2c_del_driver(&dash_fastcg_driver); +} +module_exit(dash_fastcg_exit); diff --git a/drivers/power/supply/qcom/op_dash_adapter.c b/drivers/power/supply/qcom/op_dash_adapter.c new file mode 100644 index 000000000000..867222f5ab86 --- /dev/null +++ b/drivers/power/supply/qcom/op_dash_adapter.c @@ -0,0 +1,599 @@ +#include +#include +#include +#include +#include +#include "op_dash_adapter.h" +/* add for dash adapter update */ +#include + +static struct op_adapter_chip *the_chip; + +#define DEFALUT_TX_VALUE 0xFF +static void dash_uart_gpio_set_value(struct op_adapter_chip *chip, + unsigned long pin, bool value) +{ + if (chip->tx_invalid_val != value) { + gpio_set_value(pin, value); + chip->tx_invalid_val = value; + } +} + +#define update_cycle 998 +#define LOG_COUNT 30 +int rx_time[LOG_COUNT]; +int tx_time[LOG_COUNT]; +void print_oem(void) +{ + int i; + + for (i = 0; i < LOG_COUNT - 1; i++) + pr_info("rx=%d, tx=%d\n", rx_time[i], tx_time[i]); +} + +static int dash_uart_gpio_get_value(unsigned long pin) +{ + return gpio_get_value(pin); +} + +void oem_delay(struct op_adapter_chip *chip, cycles_t begin_time) +{ + cycles_t time, curl_time; + + if (chip->timer_delay == 52) { + do { + curl_time = get_cycles(); + time = curl_time - begin_time; + } while (time < update_cycle); + } else if (chip->timer_delay == 78) { + do { + curl_time = get_cycles(); + time = curl_time - begin_time; + } while (time < update_cycle * 3 / 2); + } else if (chip->timer_delay == 25) { + do { + curl_time = get_cycles(); + time = curl_time - begin_time; + } while (time < update_cycle / 2); + } else if (chip->timer_delay == 2) { + do { + curl_time = get_cycles(); + time = curl_time - begin_time; + } while (time < 30); + } else { + udelay(chip->timer_delay); + } +} + +static void dash_uart_tx_bit( + struct op_adapter_chip *chip, unsigned char tx_data) +{ + static unsigned char tx_bit = BIT_START; + + switch (tx_bit) { + case BIT_START: + chip->tx_byte_over = false; + dash_uart_gpio_set_value( + chip, chip->uart_tx_gpio, 0); + tx_bit = BIT_0; + break; + case BIT_0: + case BIT_1: + case BIT_2: + case BIT_3: + case BIT_4: + case BIT_5: + case BIT_6: + case BIT_7: + if (tx_data & (1 << tx_bit)) + dash_uart_gpio_set_value( + chip, chip->uart_tx_gpio, 1); + else + dash_uart_gpio_set_value( + chip, chip->uart_tx_gpio, 0); + tx_bit++; + break; + case BIT_STOP: + case BIT_IDLE: + dash_uart_gpio_set_value(chip, chip->uart_tx_gpio, 1); + tx_bit = BIT_START; + chip->tx_byte_over = true; + break; + default: + break; + } +} + +static int dash_uart_rx_bit(struct op_adapter_chip *chip) +{ + static unsigned char rx_bit = BIT_IDLE, rx_val; + + switch (rx_bit) { + case BIT_IDLE: + chip->rx_byte_over = false; + if (!dash_uart_gpio_get_value(chip->uart_rx_gpio)) { + rx_bit = BIT_0; + chip->timer_delay = 78; /* 1.5 cycle */ + } else { + chip->timer_delay = 2; /* 0.02 cycle */ + } + break; + case BIT_0: + case BIT_1: + case BIT_2: + case BIT_3: + case BIT_4: + case BIT_5: + case BIT_6: + case BIT_7: + chip->timer_delay = 52; /* 1 cycle */ + if (dash_uart_gpio_get_value(chip->uart_rx_gpio)) + rx_val |= (1 << rx_bit); + else + rx_val &= ~(1 << rx_bit); + rx_bit++; + break; + case BIT_STOP: + rx_bit = BIT_IDLE; + chip->rx_byte_over = true; + break; + default: + break; + } + + return rx_val; +} + +static void dash_uart_tx_byte( + struct op_adapter_chip *chip, unsigned char tx_data) +{ + cycles_t time, curl_time, begin_time; + int i = 0; + + chip->timer_delay = 52; + while (1) { + begin_time = get_cycles(); + mb();/*need const tx*/ + dash_uart_tx_bit(chip, tx_data); + curl_time = get_cycles(); + time = curl_time - begin_time; + tx_time[i++] = time; + + if (i > LOG_COUNT - 1) + i = 0; + + oem_delay(chip, begin_time); + if (chip->tx_byte_over) { + chip->timer_delay = 25; + break; + } + } +} + +static unsigned char dash_uart_rx_byte( + struct op_adapter_chip *chip, unsigned int cmd) +{ + unsigned char rx_val = 0; + unsigned int count = 0; + unsigned int max_count = 0; + cycles_t time, curl_time, begin_time; + int i = 0; + + if (cmd == Read_Addr_Line_Cmd) + max_count = Read_Addr_Line_Cmd_Count; + else if (cmd == Write_Addr_Line_Cmd) + max_count = Write_Addr_Line_Cmd_Count; + else if (cmd == Erase_Addr_Line_Cmd) + max_count = Erase_Addr_Line_Cmd_Count; + else if (cmd == Read_All_Cmd) + max_count = Read_All_Cmd_Count; + else if (cmd == Erase_All_Cmd) + max_count = Erase_All_Cmd_Count; + else if (cmd == Boot_Over_Cmd) + max_count = Boot_Over_Cmd_Count; + else + max_count = Other_Cmd_count; + + chip->rx_timeout = false; + chip->timer_delay = 25; + while (1) { + begin_time = get_cycles(); + mb();/*need a const rx*/ + rx_val = dash_uart_rx_bit(chip); + curl_time = get_cycles(); + time = curl_time - begin_time; + rx_time[i++] = time; + + if (i > LOG_COUNT - 1) + i = 0; + + oem_delay(chip, begin_time); + if (chip->rx_byte_over) + return rx_val; + if (count > max_count) { + chip->rx_timeout = true; + return 0x00; + } + count++; + } +} + +static void dash_uart_irq_fiq_enable(bool enable) +{ + if (enable) { + preempt_enable(); + local_fiq_enable(); + local_irq_enable(); + } else { + local_irq_disable(); + local_fiq_disable(); + preempt_disable(); + } +} + +static int dash_uart_write_some_addr( + struct op_adapter_chip *chip, u8 *fw_buf, int length) +{ + unsigned int write_addr = 0, i = 0, fw_count = 0; + unsigned char rx_val = 0; + + while (1) { + /* tx: 0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Write_Addr_Line_Cmd >> 8) & 0xff); + /* tx: 0x02 */ + dash_uart_tx_byte(chip, Write_Addr_Line_Cmd & 0xff); + /* count:16 bytes */ + dash_uart_tx_byte(chip, 16); + + /* addr: 2 byte */ + if (write_addr == 0) + write_addr = + (fw_buf[fw_count + 1] << 8) + | fw_buf[fw_count]; + dash_uart_tx_byte(chip, (write_addr >> 8) & 0xff); + dash_uart_tx_byte(chip, write_addr & 0xff); + + if (!(write_addr % 0x20)) + fw_count += 2; + + /* data: 16 bytes */ + for (i = 0; i < 16; i++) { + dash_uart_tx_byte(chip, fw_buf[fw_count]); + fw_count++; + + if (i == 15) + rx_val = dash_uart_rx_byte( + chip, Write_Addr_Line_Cmd); + } + + dash_uart_irq_fiq_enable(true); + write_addr += 16; + if (rx_val != UART_ACK || chip->rx_timeout) { + pr_err("%s err,write_addr:0x%x,chip->rx_timeout:%d,rx_val=%d\n", + __func__, write_addr, + chip->rx_timeout, rx_val); + return -EINVAL; + } + if (fw_count >= length) + return 0; + } +} + +#define STM8S_ADAPTER_FIRST_ADDR 0x8C00 +#define STM8S_ADAPTER_LAST_ADDR 0x9FEF +#define HALF_ONE_LINE 16 + +static bool dash_uart_read_addr_line_and_check( + struct op_adapter_chip *chip, unsigned int addr) +{ + unsigned char fw_check_buf[20] = {0x00}; + int i = 0; + static int fw_line; + bool check_result = false; + int addr_check_err = 0; + + if (addr == STM8S_ADAPTER_FIRST_ADDR) + fw_line = 0; + + /* Tx_Read_Addr_Line */ + /* tx:0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Read_Addr_Line_Cmd >> 8) & 0xff); + /* tx:0x01 */ + dash_uart_tx_byte(chip, Read_Addr_Line_Cmd & 0xff); + /* tx:0x9F */ + dash_uart_tx_byte(chip, (addr >> 8) & 0xff); + /* tx:0xF0 */ + dash_uart_tx_byte(chip, addr & 0xff); + + fw_check_buf[0] = dash_uart_rx_byte(chip, Read_Addr_Line_Cmd); + if (chip->rx_timeout) + goto read_addr_line_err; + + fw_check_buf[1] = dash_uart_rx_byte(chip, Read_Addr_Line_Cmd); + if (chip->rx_timeout) + goto read_addr_line_err; + + if (addr != ((fw_check_buf[0] << 8) | fw_check_buf[1])) { + addr_check_err = 1; + goto read_addr_line_err; + } + + for (i = 0; i < 16; i++) { + fw_check_buf[i + 2] = + dash_uart_rx_byte(chip, Read_Addr_Line_Cmd); + if (chip->rx_timeout) + goto read_addr_line_err; + } + + if (!(addr % 0x20)) { + if (addr == ((adapter_stm8s_firmware_data[fw_line * 34 + 1] + << 8) + | (adapter_stm8s_firmware_data[fw_line * 34]))) { + for (i = 0; i < 16; i++) { + if (fw_check_buf[i + 2] + != adapter_stm8s_firmware_data[ + fw_line * 34 + 2 + i]) + goto read_addr_line_err; + } + } + } else { + if ((addr - 16) == + ((adapter_stm8s_firmware_data[fw_line * 34 + 1] << 8) + | (adapter_stm8s_firmware_data[fw_line * 34]))) { + for (i = 0; i < 16; i++) { + if (fw_check_buf[i + 2] + != adapter_stm8s_firmware_data[ + fw_line * 34 + + 2 + HALF_ONE_LINE + i]) + goto read_addr_line_err; + } + } + fw_line++; + } + check_result = true; + +read_addr_line_err: + dash_uart_irq_fiq_enable(true); + if (addr_check_err) { + pr_err("%s addr:0x%x,buf[0]:0x%x,buf[1]:0x%x\n", + __func__, addr, fw_check_buf[0], fw_check_buf[1]); + } + if (!check_result) { + pr_err("%s fw_check err,addr:0x%x,check_buf[%d]:0x%x != fw_data[%d]:0x%x\n", + __func__, addr, i + 2, fw_check_buf[i + 2], + (fw_line * 34 + 2 + i), + adapter_stm8s_firmware_data[fw_line * 34 + 2 + i]); + for (i = 0; i < 16; i++) + pr_err("fw_check_buf[%d]=0x%x\n", + i+2, fw_check_buf[i + 2]); + } + return check_result; +} + +static int dash_uart_read_front_addr_and_check(struct op_adapter_chip *chip) +{ + unsigned int read_addr = STM8S_ADAPTER_FIRST_ADDR; + bool result = false; + + while (read_addr < STM8S_ADAPTER_LAST_ADDR) { + result = dash_uart_read_addr_line_and_check(chip, read_addr); + read_addr = read_addr + 16; + if ((!result) || chip->rx_timeout) { + pr_err("%s result:%d,chip->rx_timeout:%d\n", + __func__, result, chip->rx_timeout); + return -EINVAL; + } + } + return 0; +} + +static bool +dash_adapter_update_handle( + struct op_adapter_chip *chip, + unsigned long tx_pin, unsigned long rx_pin) +{ + unsigned char rx_val = 0; + int rx_last_line_count = 0; + unsigned char rx_last_line[18] = {0x0}; + int rc = 0; + + pr_err("%s v1 begin\n", __func__); + chip->uart_tx_gpio = tx_pin; + chip->uart_rx_gpio = rx_pin; + chip->adapter_update_ing = true; + chip->tx_invalid_val = DEFALUT_TX_VALUE; + chip->rx_timeout = false; + + /* step1: Tx_Erase_Addr_Line */ + /* tx:0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Erase_Addr_Line_Cmd >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x03 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Erase_Addr_Line_Cmd & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x9F */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Last_Line_Addr >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0xF0 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Last_Line_Addr & 0xff); + rx_val = dash_uart_rx_byte(chip, Erase_Addr_Line_Cmd); + dash_uart_irq_fiq_enable(true); + if (rx_val != UART_ACK || chip->rx_timeout) { + pr_err("%s Tx_Erase_Addr_Line err,chip->rx_timeout:%d, rx_val:0x%x\n", + __func__, chip->rx_timeout, rx_val); + goto update_err; + } + /* Step2: Tx_Read_Addr_Line */ + /* tx:0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Read_Addr_Line_Cmd >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x01 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Read_Addr_Line_Cmd & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x9F */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Last_Line_Addr >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0xF0 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Last_Line_Addr & 0xff); + for (rx_last_line_count = 0; + rx_last_line_count < 18; rx_last_line_count++) { + rx_last_line[rx_last_line_count] + = dash_uart_rx_byte(chip, Read_Addr_Line_Cmd); + if (chip->rx_timeout) + break; + } + + dash_uart_irq_fiq_enable(true); + if ((rx_last_line[FW_EXIST_LOW] == 0x55 && + rx_last_line[FW_EXIST_HIGH] == 0x34) + || chip->rx_timeout) { + pr_err("%s Tx_Read_Addr_Line err,chip->rx_timeout:%d\n", + __func__, chip->rx_timeout); + goto update_err; + } + + /* Step3: Tx_Erase_All */ + /* tx:0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Erase_All_Cmd >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x05 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Erase_All_Cmd & 0xff); + rx_val = dash_uart_rx_byte(chip, Erase_All_Cmd); + dash_uart_irq_fiq_enable(true); + if (rx_val != UART_ACK || chip->rx_timeout) { + pr_err("%s Tx_Erase_All err,chip->rx_timeout:%d\n", + __func__, chip->rx_timeout); + goto update_err; + } + + /* Step4: Tx_Write_Addr_Line */ + rc = dash_uart_write_some_addr(chip, &adapter_stm8s_firmware_data[0], + (sizeof(adapter_stm8s_firmware_data) - 34)); + if (rc) { + pr_err("%s Tx_Write_Addr_Line err\n", __func__); + goto update_err; + } + + /* Step5: Tx_Read_All */ + rc = dash_uart_read_front_addr_and_check(chip); + if (rc) { + pr_err("%s Tx_Read_All err\n", __func__); + goto update_err; + } + + /* Step6: write the last line */ + rc = dash_uart_write_some_addr(chip, + &adapter_stm8s_firmware_data[ + sizeof(adapter_stm8s_firmware_data) - 34], + 34); + if (rc) { + pr_err("%s write the last line err\n", __func__); + goto update_err; + } + + /* Step7: Tx_Boot_Over */ + /* tx:0xF5 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, (Boot_Over_Cmd >> 8) & 0xff); + dash_uart_irq_fiq_enable(true); + + /* tx:0x06 */ + dash_uart_irq_fiq_enable(false); + dash_uart_tx_byte(chip, Boot_Over_Cmd & 0xff); + rx_val = dash_uart_rx_byte(chip, Boot_Over_Cmd); + dash_uart_irq_fiq_enable(true); + if (rx_val != UART_ACK || chip->rx_timeout) { + pr_err("%s Tx_Boot_Over err,chip->rx_timeout:%d\n", + __func__, chip->rx_timeout); + goto update_err; + } + chip->rx_timeout = false; + chip->adapter_update_ing = false; + pr_err("%s success\n", __func__); + return true; + +update_err: + chip->rx_timeout = false; + chip->adapter_update_ing = false; + print_oem(); + pr_err("%s err\n", __func__); + return false; +} + +bool dash_adapter_update_is_tx_gpio(unsigned long gpio_num) +{ + if (!the_chip) + return false; + if (the_chip->adapter_update_ing && gpio_num == the_chip->uart_tx_gpio) + return true; + else + return false; +} + +bool dash_adapter_update_is_rx_gpio(unsigned long gpio_num) +{ + if (!the_chip) + return false; + + if (the_chip->adapter_update_ing && gpio_num == the_chip->uart_rx_gpio) + return true; + else + return false; +} + + +struct op_adapter_operations op_adapter_ops = { + .adapter_update = dash_adapter_update_handle, +}; + +static int __init dash_adapter_init(void) +{ + struct op_adapter_chip *chip = NULL; + + chip = kzalloc(sizeof(struct op_adapter_chip), GFP_KERNEL); + if (!chip) + return -EINVAL; + + chip->timer_delay = 0; + chip->tx_byte_over = false; + chip->rx_byte_over = false; + chip->rx_timeout = false; + chip->uart_tx_gpio = 0; + chip->uart_rx_gpio = 0; + chip->adapter_update_ing = false; + chip->adapter_firmware_data = adapter_stm8s_firmware_data; + chip->adapter_fw_data_count = sizeof(adapter_stm8s_firmware_data); + chip->vops = &op_adapter_ops; + op_adapter_init(chip); + the_chip = chip; + + pr_info("%s success\n", __func__); + return 0; +} + +static void __init dash_adapter_exit(void) +{ + +} + +module_init(dash_adapter_init); +module_exit(dash_adapter_exit); diff --git a/drivers/power/supply/qcom/op_dash_adapter.h b/drivers/power/supply/qcom/op_dash_adapter.h new file mode 100644 index 000000000000..09489c806f83 --- /dev/null +++ b/drivers/power/supply/qcom/op_dash_adapter.h @@ -0,0 +1,223 @@ +#ifndef _OP_DASH_ADAPTER_H_ +#define _OP_DASH_ADAPTER_H_ + +/*ap_data---->rx ap_clk---->tx*/ +#define BIT_0 0 +#define BIT_1 1 +#define BIT_2 2 +#define BIT_3 3 +#define BIT_4 4 +#define BIT_5 5 +#define BIT_6 6 +#define BIT_7 7 +#define BIT_STOP 8 +#define BIT_IDLE 9 +#define BIT_START 10 + +#define UART_NOACK 0 +#define UART_ACK 1 + +#define FW_VER_LOW 14 +#define FW_VER_HIGH 15 +#define FW_EXIST_LOW 16 +#define FW_EXIST_HIGH 17 + +#define Tx_Read_Addr_Line 0x01 +#define Tx_Write_Addr_Line 0x02 +#define Tx_Erase_Addr_Line 0x03 +#define Tx_Read_All 0x04 +#define Tx_Erase_All 0x05 +#define Tx_Boot_Over 0x06 +#define Tx_Cmd_Invalid 0xff + +#define Rx_Read_Addr_Line 0x01 +#define Rx_Write_Addr_Line 0x02 +#define Rx_Erase_Addr_Line 0x03 +#define Rx_Read_All 0x04 +#define Rx_Erase_All 0x05 +#define Rx_Boot_Over 0x06 +#define Rx_Cmd_Invalid 0xff + +#define Read_Addr_Line_Cmd 0xF501 +#define Write_Addr_Line_Cmd 0xF502 +#define Erase_Addr_Line_Cmd 0xF503 +#define Read_All_Cmd 0xF504 +#define Erase_All_Cmd 0xF505 +#define Boot_Over_Cmd 0xF506 +#define Last_Line_Addr 0x9FF0 + +#define Read_Addr_Line_Cmd_Count 2000 /*physical test: 27*/ +#define Write_Addr_Line_Cmd_Count 18000 /*physical test:8720*/ +#define Erase_Addr_Line_Cmd_Count 16000 /*physical test:7657*/ +#define Read_All_Cmd_Count 2000 /*physical test:27*/ +#define Erase_All_Cmd_Count 180000 /*physical test:86916*/ +#define Boot_Over_Cmd_Count 2000 /*physical test:34*/ +#define Other_Cmd_count 2000 + +#include +extern void op_adapter_init(struct op_adapter_chip *chip); +/*fw_ver must be 0x0b/0x0c/0x0d/0x0e/0x0f because of pic1508/stm8s*/ + +static unsigned char adapter_stm8s_firmware_data[] = { +0x00,0x8c,0x82,0x00,0x9F,0x20,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF, +0x20,0x8C,0x82,0x00,0x93,0xBC,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF, +0x40,0x8C,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF, +0x60,0x8C,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF,0x82,0x00,0x9F,0xCF, +0x80,0x8C,0x3B,0x00,0x08,0x3F,0x08,0x20,0x0C,0x3D,0x08,0x26,0x03,0xCD,0x93,0x70,0xA6,0x14,0xCD,0x9E,0xA4,0xB6,0x08,0xA1,0x02,0x27,0x2A,0xCD,0x9F,0xC1,0xCD,0x91,0x69,0xAE, +0xA0,0x8C,0x00,0x12,0xCD,0x91,0x2C,0xB7,0x08,0xA1,0x02,0x26,0x04,0xA6,0x01,0x20,0x14,0xA1,0x01,0x26,0x05,0xCD,0x92,0xDD,0x20,0xD6,0xA1,0x03,0x26,0xCB,0xA6,0x01,0xCD,0x92, +0xC0,0x8C,0xF6,0x20,0xCB,0x32,0x00,0x08,0x81,0xCD,0x9A,0x2A,0xCD,0x99,0xF2,0xBF,0x0A,0x90,0xBF,0x08,0xB7,0x0D,0x3F,0x0C,0xCD,0x9F,0xC1,0xB6,0x0D,0xCD,0x8D,0xAE,0xA6,0x02, +0xE0,0x8C,0xCD,0x93,0x29,0xBF,0x00,0xB3,0x0A,0x2F,0x06,0xBE,0x08,0xB3,0x00,0x2E,0x74,0xBE,0x00,0xB3,0x0A,0x2E,0x5C,0x3D,0x0D,0x27,0x49,0xA6,0x04,0xCD,0x93,0x29,0xA3,0x07, +0x00,0x8D,0x03,0x24,0x60,0xC6,0x00,0x31,0xA1,0x05,0x24,0x09,0xC6,0x00,0x31,0x4C,0xC7,0x00,0x31,0x20,0x15,0xC6,0x00,0x32,0xA1,0x0A,0x24,0x0E,0xC6,0x00,0x32,0x4C,0xC7,0x00, +0x20,0x8D,0x32,0xCD,0x92,0xDD,0x72,0x5F,0x00,0x31,0xC6,0x00,0x32,0xA1,0x0A,0x26,0x0A,0x35,0x0B,0x00,0x32,0x35,0x05,0x00,0x31,0x20,0x2A,0xC6,0x00,0x32,0xA1,0x0B,0x25,0x23, +0x40,0x8D,0x20,0x0A,0xA6,0x04,0xCD,0x93,0x29,0xA3,0x07,0x2C,0x24,0x40,0xCD,0x92,0xDD,0x20,0x12,0xBE,0x08,0xB3,0x00,0x2E,0x0C,0xB6,0x0D,0xCD,0x92,0xF6,0x20,0x05,0xA6,0x02, +0x60,0x8D,0xCD,0x9E,0xA4,0xB6,0x0C,0x4C,0xB7,0x0C,0xA6,0x02,0xCD,0x93,0x29,0xA3,0x05,0xBF,0x2F,0x14,0xC6,0x00,0x28,0xAB,0xF7,0xCD,0x93,0x1B,0xB6,0x0D,0xCD,0x8D,0xAE,0xA6, +0x80,0x8D,0x14,0xCD,0x9E,0xA4,0x20,0x06,0xB6,0x0C,0xA1,0x0A,0x25,0xD2,0xA6,0x04,0xCD,0x93,0x29,0x3D,0x0D,0x27,0x06,0x90,0xAE,0x07,0x2B,0x20,0x04,0x90,0xAE,0x07,0x4E,0xBF, +0xA0,0x8D,0x00,0x90,0xB3,0x00,0x2E,0x05,0xB6,0x0D,0xCD,0x92,0xF6,0xCC,0x9A,0x7E,0xA1,0x01,0x27,0x2F,0xA6,0x04,0xCD,0x93,0x29,0xA3,0x05,0x54,0x24,0x21,0xA6,0x02,0xCD,0x93, +0xC0,0x8D,0x29,0xA3,0x02,0x8F,0x25,0x17,0xC6,0x00,0x30,0xA1,0x46,0x24,0x08,0xC6,0x00,0x30,0x4C,0xC7,0x00,0x30,0x81,0xCD,0x8E,0xAD,0xCD,0x8D,0xE2,0x20,0xFB,0x72,0x5F,0x00, +0xE0,0x8D,0x30,0x81,0xCD,0x99,0xED,0x3B,0x00,0x0A,0x52,0x04,0x35,0x01,0x00,0x0A,0x90,0xAE,0x9F,0xCB,0x96,0x5C,0x89,0xA6,0x04,0xCD,0x9F,0x5F,0x85,0xCD,0x9F,0xC1,0xCD,0x8F, +0x00,0x8E,0x12,0x7B,0x01,0xA1,0x1D,0x26,0x12,0x7B,0x02,0xA1,0x05,0x26,0x0C,0x7B,0x03,0xA1,0x14,0x26,0x06,0x7B,0x04,0xA1,0x08,0x27,0x4B,0xCD,0x9F,0xC1,0x7B,0x01,0xA1,0x1D, +0x20,0x8E,0x27,0x13,0xCD,0x9F,0xC1,0x96,0x5C,0xCD,0x8E,0xC0,0x96,0x1C,0x00,0x02,0xBF,0x08,0x4D,0x26,0xE9,0x20,0x37,0xBE,0x08,0xCD,0x8E,0xC0,0xBE,0x08,0x5C,0xBF,0x08,0x4D, +0x40,0x8E,0x26,0x02,0x3F,0x0A,0xCD,0x8E,0xC0,0xBE,0x08,0x5C,0xBF,0x08,0x4D,0x26,0x02,0x3F,0x0A,0xCD,0x8E,0xC0,0xBE,0x08,0x5C,0xBF,0x08,0x4D,0x26,0x02,0x3F,0x0A,0x3D,0x0A, +0x60,0x8E,0x26,0x9F,0x20,0x08,0x35,0x01,0x00,0x2F,0x4F,0xCD,0x8E,0x74,0x5B,0x04,0x32,0x00,0x0A,0xCC,0x9A,0x85,0x4D,0x27,0x03,0xCD,0x8E,0xAD,0xC6,0x00,0x2F,0x26,0x03,0xCD, +0x80,0x8E,0x93,0x70,0xCD,0x9F,0xC1,0xCD,0x9F,0x9B,0x72,0x1C,0x50,0x5B,0x72,0x1D,0x50,0x5C,0x72,0x5F,0x9F,0xFC,0x72,0x5F,0x9F,0xFD,0x72,0x5F,0x9F,0xFE,0x72,0x5F,0x9F,0xFF, +0xA0,0x8E,0xCD,0x9F,0xC1,0xC6,0x50,0x5F,0xA5,0x05,0x27,0xF9,0xCC,0x93,0x70,0xA6,0x01,0xCD,0x94,0x82,0xA6,0x14,0xCD,0x9E,0xA4,0xA6,0xC8,0xCD,0x93,0x1B,0x4F,0xCC,0x95,0x1B, +0xC0,0x8E,0xCD,0x99,0xED,0x3B,0x00,0x0A,0xBF,0x08,0xC6,0x52,0x30,0xB7,0x0A,0xCD,0x9F,0xC1,0x20,0x08,0xCD,0x9F,0xC1,0xC6,0x52,0x30,0xB7,0x0A,0xB6,0x0A,0xA5,0x20,0x27,0xF2, +0xE0,0x8E,0xB6,0x0A,0xA5,0x09,0x27,0x09,0xC6,0x52,0x31,0x92,0xC7,0x08,0x4F,0x20,0x08,0xC6,0x52,0x31,0x92,0xC7,0x08,0xA6,0x01,0x32,0x00,0x0A,0xCC,0x9A,0x85,0x72,0x5F,0x52, +0x00,0x8F,0x34,0x72,0x5F,0x52,0x36,0x72,0x5F,0x52,0x33,0x72,0x5F,0x52,0x32,0x72,0x5F,0x52,0x35,0x81,0x72,0x5F,0x50,0x12,0x72,0x5F,0x50,0x13,0x35,0x02,0x50,0x11,0x72,0x19, +0x20,0x8F,0x52,0x34,0xC6,0x52,0x36,0xC7,0x52,0x36,0x35,0x01,0x52,0x33,0x35,0x34,0x52,0x32,0xC6,0x52,0x35,0xAA,0x0C,0xC7,0x52,0x35,0x81,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92, +0x40,0x8F,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x26,0x04,0x4F,0xCC,0x9A,0x85,0xBE,0x08,0x1C,0x00,0x08,0xF6,0x48,0x88,0xBE,0x08,0x1C,0x00,0x07,0xF6,0x48,0x48,0xB7,0x01, +0x60,0x8F,0x84,0xBA,0x01,0xBE,0x08,0x1C,0x00,0x09,0xFA,0x90,0x5F,0x90,0x97,0xAE,0x00,0x52,0xBF,0x00,0x93,0xCD,0x9C,0x69,0x1C,0x03,0xD4,0xCF,0x00,0x26,0xCE,0x00,0x26,0x1C, +0x80,0x8F,0xFF,0xCF,0xCF,0x00,0x24,0xCE,0x00,0x24,0xA3,0x04,0xEB,0x2F,0x06,0xAE,0x04,0xEA,0xCF,0x00,0x24,0xCE,0x00,0x26,0xA3,0x05,0x1C,0x2F,0x06,0xAE,0x05,0x1B,0xCF,0x00, +0xA0,0x8F,0x26,0xA6,0x01,0xCC,0x9A,0x85,0x72,0x5F,0x00,0x1F,0x72,0x5F,0x00,0x20,0x35,0x01,0x00,0x21,0x35,0x01,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0x35,0x01,0x00,0x1F, +0xC0,0x8F,0x72,0x5F,0x00,0x20,0x72,0x5F,0x00,0x21,0x35,0x01,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1, +0xE0,0x8F,0x00,0x27,0x08,0xBE,0x08,0x1C,0x00,0x03,0x7D,0x26,0x04,0x4F,0xCC,0x9A,0x85,0xA6,0x01,0xCC,0x9A,0x85,0x72,0x5F,0x00,0x1F,0x35,0x01,0x00,0x20,0x35,0x01,0x00,0x21, +0x00,0x90,0x72,0x5F,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x26,0x04,0x4F,0xCC,0x9A,0x85,0xBE, +0x20,0x90,0x08,0x1C,0x00,0x04,0x7D,0x26,0x0D,0xBE,0x08,0x1C,0x00,0x05,0x7D,0x27,0x05,0xA6,0x02,0xCC,0x9A,0x85,0xA6,0x01,0xCC,0x9A,0x85,0x35,0x01,0x00,0x1F,0x72,0x5F,0x00, +0x40,0x90,0x20,0x72,0x5F,0x00,0x21,0x72,0x5F,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x27,0x15, +0x60,0x90,0xBE,0x08,0x1C,0x00,0x04,0x7D,0x26,0x0D,0xBE,0x08,0x1C,0x00,0x05,0x7D,0x27,0x05,0xA6,0x01,0xCC,0x9A,0x85,0x4F,0xCC,0x9A,0x85,0xC6,0x9F,0xFC,0xB7,0x00,0x44,0x44, +0x80,0x90,0x44,0xA4,0x01,0xC7,0x00,0x1F,0xB6,0x00,0x44,0x44,0xA4,0x01,0xC7,0x00,0x20,0xB6,0x00,0x44,0xA4,0x01,0xC7,0x00,0x21,0xB6,0x00,0xA4,0x01,0xC7,0x00,0x22,0xAE,0x00, +0xA0,0x90,0x1C,0xCC,0x92,0x9B,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x26,0x05,0xA6,0x02,0xCC,0x9A,0x85,0xBE,0x08,0x1C,0x00,0x04,0xF6, +0xC0,0x90,0xC7,0x00,0x2D,0xBE,0x08,0x1C,0x00,0x03,0x7D,0x26,0x04,0x4F,0xCC,0x9A,0x85,0xBE,0x08,0x1C,0x00,0x05,0xF6,0x4E,0xA4,0xF0,0xC7,0x00,0x29,0xBE,0x08,0x1C,0x00,0x06, +0xE0,0x90,0xF6,0x48,0x48,0x48,0xCA,0x00,0x29,0xC7,0x00,0x29,0xBE,0x08,0x1C,0x00,0x07,0xF6,0x48,0x48,0xCA,0x00,0x29,0xC7,0x00,0x29,0xBE,0x08,0x1C,0x00,0x08,0xF6,0x48,0xCA, +0x00,0x91,0x00,0x29,0xC7,0x00,0x29,0xBE,0x08,0x1C,0x00,0x09,0xF6,0xCA,0x00,0x29,0xC7,0x00,0x29,0xA6,0x01,0xCC,0x9A,0x85,0x72,0x5F,0x00,0x1F,0x35,0x01,0x00,0x20,0x72,0x5F, +0x20,0x91,0x00,0x21,0x72,0x5F,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xCD,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x27,0x28,0xBE,0x08,0x1C, +0x40,0x91,0x00,0x04,0xF6,0x48,0xBE,0x08,0x1C,0x00,0x05,0xFA,0xA1,0x03,0x26,0x05,0xA6,0x02,0xCC,0x9A,0x85,0xA1,0x02,0x26,0x05,0xA6,0x03,0xCC,0x9A,0x85,0xA1,0x01,0x26,0x05, +0x60,0x91,0xA6,0x01,0xCC,0x9A,0x85,0x4F,0xCC,0x9A,0x85,0x72,0x5F,0x00,0x1F,0x72,0x5F,0x00,0x20,0x35,0x01,0x00,0x21,0x72,0x5F,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xCD, +0x80,0x91,0x99,0xED,0xBF,0x08,0xCD,0x92,0x4D,0xBE,0x08,0xCD,0x92,0x33,0xA1,0x00,0x26,0x03,0xCC,0x92,0x19,0xBE,0x08,0x1C,0x00,0x03,0x7D,0x27,0x7E,0xBE,0x08,0x1C,0x00,0x05, +0xA0,0x91,0xF6,0x5F,0x97,0xCD,0x9C,0xA9,0xBF,0x00,0xBE,0x08,0x1C,0x00,0x04,0xF6,0x5F,0x97,0xCD,0x9C,0xA8,0x01,0xBA,0x01,0x01,0xBA,0x00,0x01,0x90,0x93,0xBE,0x08,0x1C,0x00, +0xC0,0x91,0x06,0xF6,0x5F,0x97,0x58,0x58,0x58,0xBF,0x00,0x93,0x01,0xBA,0x01,0x01,0xBA,0x00,0x01,0x90,0x93,0xBE,0x08,0x1C,0x00,0x07,0xF6,0x5F,0x97,0x58,0x58,0xBF,0x00,0x93, +0xE0,0x91,0x01,0xBA,0x01,0x01,0xBA,0x00,0x01,0x90,0x93,0xBE,0x08,0x1C,0x00,0x08,0xF6,0x5F,0x97,0x58,0xBF,0x00,0x93,0x01,0xBA,0x01,0x01,0xBA,0x00,0x01,0x90,0xBE,0x08,0x72, +0x00,0x92,0xA9,0x00,0x09,0x90,0xF6,0x90,0x5F,0x90,0x97,0x90,0xBF,0x00,0x01,0xBA,0x01,0x02,0xCD,0x9C,0xA9,0x1C,0x0D,0x4C,0xCC,0x9A,0x85,0x5F,0xCC,0x9A,0x85,0x72,0x5F,0x00, +0x20,0x92,0x1F,0x72,0x5F,0x00,0x20,0x72,0x5F,0x00,0x21,0x35,0x01,0x00,0x22,0xAE,0x00,0x1C,0xCC,0x92,0x9B,0xF6,0xA1,0x01,0x26,0x13,0x90,0x93,0x90,0x5C,0x90,0x7D,0x26,0x0B, +0x40,0x92,0x1C,0x00,0x02,0xF6,0xA1,0x01,0x26,0x03,0xA6,0x01,0x81,0x4F,0x81,0xCD,0x9A,0x2A,0x3B,0x00,0x0C,0xBF,0x08,0xCD,0x94,0xD9,0xCD,0x9F,0x52,0x3F,0x0C,0x20,0x19,0xCD, +0x60,0x92,0x9F,0x6C,0x5F,0x41,0xB6,0x0C,0x41,0x72,0xBB,0x00,0x08,0xBF,0x0A,0xCD,0x94,0xA0,0x92,0xC7,0x0A,0xB6,0x0C,0x4C,0xB7,0x0C,0xB6,0x0C,0xA1,0x0A,0x24,0x17,0x4F,0xCD, +0x80,0x92,0x94,0xE6,0xCD,0x9F,0x45,0xA6,0x01,0xCD,0x94,0xE6,0x3D,0x0C,0x26,0xD1,0xA6,0x04,0xCD,0x9E,0xA4,0x20,0xCD,0x32,0x00,0x0C,0xCC,0x9A,0x7A,0xCD,0x99,0xED,0x3B,0x00, +0xA0,0x92,0x0A,0xBF,0x08,0xCD,0x94,0xC8,0xCD,0x94,0xF3,0xCD,0x9F,0x52,0x3F,0x0A,0x20,0x21,0x4F,0xCD,0x94,0xE6,0x5F,0x41,0xB6,0x0A,0x41,0x72,0xBB,0x00,0x08,0xF6,0xCD,0x94, +0xC0,0x92,0xAA,0xCD,0x9F,0x45,0xA6,0x01,0xCD,0x94,0xE6,0xCD,0x9F,0x6C,0xB6,0x0A,0x4C,0xB7,0x0A,0xB6,0x0A,0xA1,0x08,0x25,0xD9,0x32,0x00,0x0A,0xCC,0x9A,0x85,0xC6,0x00,0x28, +0xE0,0x92,0xA1,0xFF,0x27,0x07,0xC6,0x00,0x28,0x4C,0xC7,0x00,0x28,0xAE,0x00,0x28,0x35,0x01,0x00,0x00,0x4F,0xCC,0x9A,0x96,0x4D,0x26,0x0A,0xA6,0x04,0xCD,0x93,0x29,0xA3,0x04, +0x00,0x93,0xC7,0x25,0x17,0xC6,0x00,0x28,0x27,0x07,0xC6,0x00,0x28,0x4A,0xC7,0x00,0x28,0xAE,0x00,0x28,0x35,0x01,0x00,0x00,0x4F,0xCD,0x9A,0x96,0x81,0xC7,0x00,0x28,0xAE,0x00, +0x20,0x93,0x28,0x35,0x01,0x00,0x00,0x4F,0xCC,0x9A,0x96,0xCD,0x9A,0x2A,0xB7,0x08,0xA6,0x03,0xCD,0x93,0x59,0xBF,0x0A,0x26,0x04,0x5F,0x5C,0xBF,0x0A,0xB6,0x08,0xCD,0x93,0x59, +0x40,0x93,0xBF,0x02,0x5F,0xBF,0x00,0xBF,0x08,0xCD,0x98,0x07,0x00,0x00,0x09,0xC4,0xCD,0x9A,0x89,0xCD,0x98,0x91,0xBE,0x02,0xCC,0x9A,0x7A,0xCD,0x99,0xED,0xB7,0x08,0xCD,0x9F, +0x60,0x93,0xC1,0xB6,0x08,0xCD,0x9C,0xAE,0xBF,0x08,0xCD,0x9F,0xC1,0xBE,0x08,0xCC,0x9A,0x85,0x35,0x7F,0x50,0xD2,0x35,0x80,0x50,0xD1,0x35,0x7F,0x50,0xD2,0x81,0xCD,0x95,0x28, +0x80,0x93,0xCD,0x95,0x0E,0xCD,0x94,0xD9,0xCD,0x94,0x71,0xCD,0x94,0x8F,0xCD,0x94,0xC8,0xCD,0x94,0xF3,0xA6,0x01,0xCD,0x94,0xAA,0xA6,0x01,0xCD,0x94,0xE6,0xCD,0x94,0x53,0x35, +0xA0,0x93,0x01,0x00,0x1C,0x72,0x5F,0x00,0x1D,0x35,0x01,0x00,0x1E,0x72,0x5F,0x00,0x2A,0x72,0x5F,0x00,0x2B,0x72,0x5F,0x00,0x2D,0x72,0x5F,0x00,0x2F,0x81,0x8A,0x84,0xA4,0xBF, +0xC0,0x93,0x88,0x86,0xCD,0x9A,0x20,0xCD,0x9A,0x25,0xC6,0x00,0x2A,0xA1,0x01,0x27,0x5E,0xC6,0x00,0x2B,0x4C,0xC7,0x00,0x2B,0xC6,0x00,0x2B,0xA1,0x33,0x24,0x50,0xA6,0x02,0xCD, +0xE0,0x93,0x95,0x39,0xCD,0x95,0x04,0xB7,0x00,0xC6,0x00,0x10,0xA1,0x02,0x26,0x05,0xB6,0x00,0xC7,0x00,0x10,0x3D,0x00,0x26,0x07,0xC6,0x00,0x10,0xA1,0x01,0x27,0x0B,0xB6,0x00, +0x00,0x94,0xA1,0x01,0x26,0x0C,0xC6,0x00,0x10,0x26,0x07,0xC6,0x00,0x2C,0x4C,0xC7,0x00,0x2C,0xC6,0x00,0x2C,0xA1,0x14,0x25,0x11,0x72,0x5F,0x00,0x2C,0x35,0x02,0x00,0x10,0x35, +0x20,0x94,0x01,0x00,0x2A,0xCD,0x94,0x34,0x20,0x05,0xB6,0x00,0xC7,0x00,0x10,0xCD,0x9A,0x5C,0xCD,0x9A,0x51,0x80,0xCD,0x94,0xF3,0xA6,0x62,0xCC,0x95,0x39,0x9B,0x72,0x1B,0x50, +0x40,0x94,0x11,0x72,0x1B,0x50,0x12,0x72,0x1A,0x50,0x13,0xC6,0x50,0xA0,0xA4,0x3F,0xC7,0x50,0xA0,0x9B,0x81,0x9B,0x72,0x1B,0x50,0x11,0x72,0x1B,0x50,0x12,0x72,0x1A,0x50,0x13, +0x60,0x94,0xC6,0x50,0xA0,0xA4,0x3F,0xC7,0x50,0xA0,0x72,0x1C,0x50,0xA0,0x9A,0x81,0xCC,0x94,0xB7,0x72,0x1E,0x50,0x0C,0x72,0x1E,0x50,0x0D,0x72,0x1F,0x50,0x0E,0x72,0x1F,0x50, +0x80,0x94,0x0A,0x81,0x4D,0x27,0x05,0x72,0x16,0x50,0x0A,0x81,0x72,0x17,0x50,0x0A,0x81,0x72,0x16,0x50,0x0C,0x72,0x16,0x50,0x0D,0x72,0x17,0x50,0x0E,0x72,0x17,0x50,0x0A,0x81, +0xA0,0x94,0x72,0x0D,0x50,0x10,0x03,0xA6,0x01,0x81,0x4F,0x81,0x4D,0x27,0x05,0x72,0x1C,0x50,0x0F,0x81,0x72,0x1D,0x50,0x0F,0x81,0x72,0x1C,0x50,0x11,0x72,0x1C,0x50,0x12,0x72, +0xC0,0x94,0x1D,0x50,0x13,0x72,0x1C,0x50,0x0F,0x81,0x72,0x1C,0x50,0x11,0x72,0x1C,0x50,0x12,0x72,0x1D,0x50,0x13,0x72,0x1D,0x50,0x0F,0x81,0x72,0x1D,0x50,0x11,0x72,0x1D,0x50, +0xE0,0x94,0x12,0x72,0x1D,0x50,0x13,0x81,0x4D,0x27,0x05,0x72,0x1A,0x50,0x0F,0x81,0x72,0x1B,0x50,0x0F,0x81,0x72,0x1A,0x50,0x11,0x72,0x1A,0x50,0x12,0x72,0x1B,0x50,0x13,0x72, +0x00,0x95,0x1B,0x50,0x0F,0x81,0x72,0x0B,0x50,0x10,0x03,0xA6,0x01,0x81,0x4F,0x81,0x72,0x1B,0x50,0x11,0x72,0x1B,0x50,0x12,0x72,0x1B,0x50,0x13,0x81,0x4D,0x27,0x05,0x72,0x18, +0x20,0x95,0x50,0x0F,0x81,0x72,0x19,0x50,0x0F,0x81,0x72,0x18,0x50,0x11,0x72,0x18,0x50,0x12,0x72,0x19,0x50,0x13,0x72,0x19,0x50,0x0F,0x81,0xCD,0x99,0xED,0xB7,0x09,0x3F,0x08, +0x40,0x95,0x4F,0xB7,0x08,0xB6,0x08,0xB1,0x09,0x24,0x0B,0xCD,0x9F,0x6C,0xB6,0x08,0xAB,0x01,0xB7,0x08,0x20,0xEF,0xCC,0x9A,0x85,0xCD,0x9A,0x2A,0xCD,0x9A,0x2F,0x52,0x05,0x4F, +0x60,0x95,0x6B,0x03,0x6B,0x02,0x3F,0x0C,0x3F,0x0D,0x6B,0x01,0x3F,0x0A,0x3F,0x0B,0xCD,0x9F,0xC1,0xA6,0x28,0xCD,0x9E,0xA4,0xCD,0x8F,0xA6,0xAE,0x00,0x12,0xCD,0x8F,0x39,0xA1, +0x80,0x95,0x00,0x26,0x03,0xCD,0x93,0x70,0x4F,0xCD,0x94,0x82,0xC6,0x00,0x29,0x90,0x5F,0x90,0x97,0xAE,0x00,0x05,0xBF,0x00,0x93,0xCD,0x9C,0x69,0x1C,0x01,0x04,0x1F,0x04,0x20, +0xA0,0x95,0x10,0x0D,0x01,0x26,0x03,0xCD,0x8E,0xAD,0xA6,0x01,0x6B,0x01,0xA6,0x1E,0xCD,0x9E,0xA4,0xCD,0x9F,0xC1,0xA6,0x0F,0xCD,0x9E,0xA4,0xCD,0x92,0x1D,0xAE,0x00,0x12,0xCD, +0xC0,0x95,0x91,0x7F,0xBF,0x08,0x26,0x03,0xCC,0x97,0x01,0xA3,0x11,0x2C,0x26,0x06,0x35,0x01,0x00,0x0A,0x20,0x13,0x3D,0x0A,0x27,0x0B,0xA3,0x0D,0x9C,0x26,0x06,0x35,0x01,0x00, +0xE0,0x95,0x0B,0x20,0x04,0x3F,0x0A,0x3F,0x0B,0x3D,0x0A,0x27,0x04,0x3D,0x0B,0x26,0xB2,0x0D,0x01,0x26,0xAE,0xB6,0x0D,0xA1,0x1E,0x24,0x06,0x4C,0xB7,0x0D,0xCC,0x96,0x82,0x3F, +0x00,0x96,0x0D,0xA6,0x02,0xCD,0x93,0x29,0x1C,0xFE,0x16,0x90,0xAE,0x00,0x52,0xCD,0x9C,0x87,0xA3,0x00,0x00,0x2F,0x10,0x1C,0x00,0x07,0xBF,0x00,0x1E,0x04,0x57,0x57,0xCD,0x9C, +0x20,0x96,0x69,0xBF,0x0E,0x20,0x08,0x1E,0x04,0x57,0x72,0xFB,0x04,0xBF,0x0E,0xCD,0x9F,0x52,0xA6,0x04,0xCD,0x93,0x29,0xBF,0x02,0xAE,0x00,0x0B,0xBF,0x00,0xBE,0x02,0xCD,0x9C, +0x40,0x96,0x69,0x90,0xAE,0x00,0x0E,0xCD,0x9C,0x87,0x90,0x93,0xBE,0x02,0x58,0xBF,0x00,0x93,0x72,0xBB,0x00,0x00,0xBF,0x00,0xBE,0x0E,0x72,0xBB,0x00,0x08,0xB3,0x00,0x2E,0x20, +0x60,0x96,0xB6,0x0C,0x4C,0xB7,0x0C,0xA1,0x0B,0x25,0x19,0xCD,0x8F,0xBC,0x4F,0xCD,0x94,0xE6,0xCD,0x9F,0x52,0xA6,0x01,0xCD,0x94,0xE6,0xCD,0x9F,0x52,0xCD,0x93,0x70,0x20,0x02, +0x80,0x96,0x3F,0x0C,0xA6,0x08,0xCD,0x9E,0xA4,0x0D,0x03,0x27,0x0F,0xA6,0x01,0x90,0xAE,0x02,0xF0,0xAE,0x02,0x8E,0xCD,0x8C,0xC7,0xCC,0x95,0xB1,0x0D,0x02,0x27,0x1E,0xBE,0x08, +0xA0,0x96,0xA3,0x10,0xE1,0x2E,0x17,0xCE,0x00,0x26,0xA3,0x04,0x36,0x2E,0x2A,0xA6,0x01,0x90,0xCE,0x00,0x26,0xCE,0x00,0x24,0xCD,0x8C,0xC7,0xCC,0x95,0xB1,0xBE,0x08,0xA3,0x10, +0xC0,0x96,0xCC,0x2F,0xEA,0x1C,0xEF,0x34,0xA3,0x00,0x15,0x24,0x1B,0xA6,0x01,0x6B,0x02,0xCE,0x00,0x26,0xA3,0x04,0x36,0x2F,0xD6,0xA6,0x01,0x90,0xAE,0x04,0x36,0xAE,0x03,0xD4, +0xE0,0x96,0xCD,0x8C,0xC7,0xCC,0x95,0xB1,0xBE,0x08,0xA3,0x10,0xE1,0x2E,0x03,0xCC,0x95,0xB1,0xA6,0x01,0x6B,0x03,0x90,0xAE,0x02,0xF0,0xAE,0x02,0x8E,0xCD,0x8C,0xC7,0xCC,0x95, +0x00,0x97,0xB1,0xCD,0x93,0x70,0xCC,0x95,0xB1,0xCD,0x9A,0x2A,0xCD,0x99,0xF2,0x3B,0x00,0x0E,0x3F,0x0C,0x3F,0x0E,0x3F,0x0D,0x3F,0x0B,0x3F,0x0A,0xCD,0x9F,0x33,0xA6,0x01,0xCD, +0x20,0x97,0x94,0x82,0xA6,0xFF,0xCD,0x93,0x1B,0xA6,0x14,0xCD,0x9E,0xA4,0x4F,0xCD,0x94,0x82,0xCD,0x9F,0xC1,0xA6,0xC8,0xCD,0x9E,0xA4,0xA6,0xC8,0xCD,0x9E,0xA4,0xCD,0x9F,0xC1, +0x40,0x97,0xA6,0xC8,0xCD,0x9E,0xA4,0xA6,0xB4,0xCD,0x9E,0xA4,0xA6,0x01,0xCD,0x95,0x1B,0x20,0x0D,0xCD,0x94,0x6E,0x3F,0x0C,0x3F,0x0E,0x3F,0x0D,0x72,0x5F,0x00,0x2E,0xCD,0x9F, +0x60,0x97,0xC1,0xCD,0x8E,0xFD,0x4F,0x90,0xAE,0x03,0x10,0xAE,0x02,0x8E,0xCD,0x8C,0xC7,0xA6,0x14,0xCD,0x9E,0xA4,0xA6,0x02,0xCD,0x93,0x29,0xBF,0x08,0xA3,0x00,0x41,0x2E,0x20, +0x80,0x97,0xB6,0x0B,0x4C,0xB7,0x0B,0xA1,0x03,0x25,0x19,0x35,0x03,0x00,0x0B,0x72,0x5F,0x00,0x2B,0x72,0x5F,0x00,0x2A,0x3F,0x0A,0x72,0x5F,0x00,0x2C,0xCD,0x94,0x53,0x20,0x02, +0xA0,0x97,0x3F,0x0B,0xBE,0x08,0xA3,0x01,0x46,0x2E,0x05,0xC6,0x00,0x2A,0x27,0xA3,0x3D,0x0E,0x26,0x12,0xB6,0x0D,0xA1,0xC8,0x24,0xA6,0xCD,0x9F,0x84,0xB7,0x0E,0xB6,0x0D,0x4C, +0xC0,0x97,0xB7,0x0D,0x20,0x9A,0xC6,0x00,0x2A,0x26,0x11,0xB6,0x0C,0xA1,0x64,0x24,0x05,0x4C,0xB7,0x0C,0x20,0x8A,0x35,0x64,0x00,0x0C,0x20,0x15,0x3D,0x0A,0x26,0x11,0x35,0x01, +0xE0,0x97,0x00,0x0A,0xA6,0xC8,0xCD,0x9E,0xA4,0xA6,0xC8,0xCD,0x9E,0xA4,0xCD,0x9F,0xC1,0xCD,0x94,0x3C,0xCD,0x98,0xFE,0xA1,0x00,0x27,0x08,0xA6,0x50,0xCD,0x9E,0xA4,0xCC,0x97, +0x00,0x98,0x5E,0x32,0x00,0x0E,0xCC,0x9A,0x7E,0xCD,0x9F,0x90,0x89,0xFE,0xBF,0x04,0x85,0xEE,0x02,0xBF,0x06,0xB6,0x00,0xBE,0x06,0x42,0x9F,0xB7,0x00,0xB6,0x01,0xBE,0x05,0x42, +0x20,0x98,0x9F,0xBB,0x00,0xB7,0x00,0xB6,0x02,0xBE,0x04,0x42,0x9F,0xBB,0x00,0xB7,0x00,0xB6,0x03,0xBE,0x03,0x42,0x9F,0xBB,0x00,0xB7,0x00,0xB6,0x01,0xBE,0x06,0x42,0x3F,0x01, +0x40,0x98,0x72,0xBB,0x00,0x00,0xBF,0x00,0xB6,0x02,0xBE,0x05,0x42,0x72,0xBB,0x00,0x00,0xBF,0x00,0xB6,0x03,0xBE,0x04,0x42,0x72,0xBB,0x00,0x00,0xBF,0x00,0xB6,0x02,0xBE,0x06, +0x60,0x98,0x42,0x3F,0x02,0x72,0xBB,0x00,0x01,0xBF,0x01,0x24,0x02,0x3C,0x00,0xB6,0x03,0xBE,0x05,0x42,0x72,0xBB,0x00,0x01,0xBF,0x01,0x24,0x02,0x3C,0x00,0xB6,0x03,0xBE,0x06, +0x80,0x98,0x42,0x3F,0x03,0x72,0xBB,0x00,0x02,0xBF,0x02,0x24,0x05,0xBE,0x00,0x5C,0xBF,0x00,0x81,0xBE,0x00,0x26,0x1E,0x90,0xBE,0x04,0x26,0x0C,0xBE,0x02,0x90,0xBE,0x06,0x65, +0xA0,0x98,0xBF,0x02,0x90,0xBF,0x06,0x81,0xBF,0x04,0x45,0x02,0x06,0x45,0x03,0x07,0xBF,0x00,0xBF,0x02,0x81,0xA6,0x20,0x3D,0x00,0x26,0x0D,0xBE,0x01,0xBF,0x00,0x45,0x03,0x02, +0xC0,0x98,0x3F,0x03,0x3A,0x03,0xA0,0x08,0x5F,0x90,0x5F,0x99,0x20,0x17,0x90,0x59,0x59,0xB3,0x04,0x26,0x03,0x90,0xB3,0x06,0x25,0x0B,0x72,0xB2,0x00,0x06,0x24,0x01,0x5A,0x72, +0xE0,0x98,0xB0,0x00,0x04,0x39,0x03,0x39,0x02,0x39,0x01,0x39,0x00,0x4A,0x2A,0xDE,0xBF,0x04,0x90,0xBF,0x06,0xBE,0x00,0x53,0xBF,0x00,0xBE,0x02,0x53,0xBF,0x02,0x81,0xCD,0x9F, +0x00,0x99,0xC1,0xCD,0x91,0x16,0xAE,0x00,0x12,0xCD,0x90,0xA4,0x4D,0x26,0x1C,0xC6,0x00,0x2D,0x26,0x03,0xCC,0x99,0xEA,0xA6,0x1E,0xCD,0x9E,0xA4,0xCD,0x90,0x79,0xAE,0x00,0x12, +0x20,0x99,0xCD,0x90,0x4F,0xC7,0x00,0x2E,0xCC,0x99,0xEA,0xA1,0x01,0x27,0x03,0xCC,0x99,0xEA,0xC6,0x00,0x2E,0x27,0x23,0xA6,0x1E,0xCD,0x9E,0xA4,0xCD,0x90,0x39,0xAE,0x00,0x12, +0x40,0x99,0xCD,0x90,0x0A,0xA1,0x02,0x26,0x0B,0x35,0x01,0x00,0x2F,0xA6,0x01,0xCD,0x8E,0x74,0x20,0x06,0x4D,0x26,0x03,0xCD,0x93,0x70,0xA6,0x1E,0xCD,0x9E,0xA4,0xC6,0x00,0x29, +0x60,0x99,0xA1,0x05,0x26,0x31,0xA6,0xFF,0xCD,0x93,0x1B,0x20,0x0E,0x35,0x02,0x00,0x11,0xA6,0x50,0xCD,0x9E,0xA4,0xA6,0x01,0xCD,0x8D,0xAE,0xCD,0x9F,0xC1,0xCD,0x8F,0xF4,0xAE, +0x80,0x99,0x00,0x12,0xCD,0x8F,0xD2,0xA1,0x00,0x26,0xE2,0xC6,0x00,0x11,0xA1,0x02,0x26,0x5A,0xCD,0x93,0x70,0x20,0xE4,0xA6,0x01,0xCD,0x94,0x82,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6, +0xA0,0x99,0xEB,0xCD,0x93,0x1B,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6,0xDC,0xCD,0x93,0x1B,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6,0xCD,0xCD,0x93,0x1B,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6,0xBE,0xCD, +0xC0,0x99,0x93,0x1B,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6,0xAF,0xCD,0x93,0x1B,0xA6,0x0A,0xCD,0x9E,0xA4,0xA6,0xA0,0xCD,0x93,0x1B,0xA6,0x14,0xCD,0x9E,0xA4,0xCD,0x8C,0x80,0xA1,0x00, +0xE0,0x99,0x27,0x05,0xCD,0x95,0x57,0x4F,0x81,0xCD,0x93,0x70,0xA6,0x01,0x81,0x88,0xA6,0x08,0x20,0x05,0x88,0xA6,0x0C,0x20,0x00,0x88,0x7B,0x02,0x88,0x7B,0x02,0x89,0x1E,0x06, +0x00,0x9A,0x1F,0x04,0x5F,0x97,0xFE,0x1F,0x06,0x85,0x84,0x81,0x89,0x1E,0x05,0xBF,0x08,0x20,0x07,0x89,0x1E,0x05,0xBF,0x0C,0x20,0x00,0x1E,0x03,0x1F,0x05,0x85,0x5B,0x02,0x81, +0x20,0x9A,0x88,0xA6,0x00,0x20,0x0F,0x88,0xA6,0x04,0x20,0x0A,0x88,0xA6,0x08,0x20,0x05,0x88,0xA6,0x0C,0x20,0x00,0x88,0x89,0x7B,0x04,0x88,0x7B,0x04,0x89,0x1E,0x08,0x1F,0x04, +0x40,0x9A,0x5F,0x97,0x1F,0x08,0xFE,0x1F,0x06,0x1E,0x08,0x5C,0x5C,0xFE,0x1F,0x08,0x85,0x84,0x81,0x89,0x1E,0x05,0xBF,0x00,0x1E,0x07,0xBF,0x02,0x20,0x16,0x89,0x1E,0x05,0xBF, +0x60,0x9A,0x04,0x1E,0x07,0xBF,0x06,0x20,0x0B,0x89,0x1E,0x05,0xBF,0x08,0x1E,0x07,0xBF,0x0A,0x20,0x00,0x1E,0x03,0x1F,0x07,0x85,0x5B,0x04,0x81,0xCD,0x9A,0x67,0x81,0xCD,0x9A, +0x80,0x9A,0x11,0xCD,0x9A,0x67,0x81,0xCD,0x9A,0x0A,0x81,0x45,0x08,0x04,0x45,0x09,0x05,0x45,0x0A,0x06,0x45,0x0B,0x07,0x81,0xCD,0x9A,0x2A,0xB7,0x0A,0x45,0x00,0x0B,0xBF,0x08, +0xA0,0x9A,0xCD,0x9F,0xC1,0x72,0x03,0x52,0x19,0x0B,0x72,0x12,0x52,0x11,0x72,0x02,0x52,0x11,0xFB,0x20,0xF0,0x72,0x10,0x52,0x11,0x72,0x01,0x52,0x17,0xFB,0x35,0x58,0x52,0x16, +0xC0,0x9A,0xCD,0x9F,0xC1,0x72,0x03,0x52,0x17,0xFB,0xC6,0x52,0x19,0x72,0x0F,0x52,0x17,0xFB,0xB6,0x0A,0xC7,0x52,0x16,0x3D,0x0B,0x26,0x18,0x20,0x22,0x72,0x0F,0x52,0x17,0xFB, +0xE0,0x9A,0x9D,0x92,0xC6,0x08,0xC7,0x52,0x16,0x9D,0x3D,0x0B,0x27,0x05,0xBE,0x08,0x5C,0xBF,0x08,0x45,0x0B,0x00,0xB6,0x00,0x4A,0xB7,0x0B,0x3D,0x00,0x26,0xDE,0xC6,0x52,0x17, +0x00,0x9B,0xA4,0x84,0xA1,0x84,0x26,0xF7,0x72,0x12,0x52,0x11,0x72,0x02,0x52,0x11,0xFB,0xCD,0x9F,0xC1,0xCC,0x9A,0x7A,0x52,0x10,0x90,0xAE,0x9F,0xA4,0x96,0x5C,0x89,0xA6,0x08, +0x20,0x9B,0xCD,0x9F,0x5F,0x85,0x90,0xAE,0x9F,0xAC,0x96,0x1C,0x00,0x09,0x89,0xA6,0x08,0xCD,0x9F,0x5F,0x85,0x96,0x5C,0xCD,0x9D,0x2F,0x96,0x1C,0x00,0x09,0xCD,0x9B,0x80,0x7B, +0x40,0x9B,0x01,0xA1,0x09,0x26,0x34,0x35,0x01,0x00,0x02,0x20,0x24,0x90,0x5F,0x61,0x93,0x50,0xBF,0x00,0x96,0x1C,0x00,0x09,0x72,0xBB,0x00,0x00,0x1C,0x00,0x08,0xF6,0x96,0x5C, +0x60,0x9B,0x90,0xBF,0x00,0x72,0xBB,0x00,0x00,0xF1,0x26,0x0F,0xB6,0x02,0x4C,0xB7,0x02,0xB6,0x02,0xA1,0x08,0x25,0xD6,0xA6,0x01,0x20,0x04,0xCD,0x9C,0x17,0x4F,0x5B,0x10,0x81, +0x80,0x9B,0xCD,0x9A,0x2A,0x3B,0x00,0x0C,0xBF,0x08,0xCD,0x9B,0xD5,0x9D,0x9D,0xCD,0x9D,0x67,0xA6,0xCC,0xCD,0x9D,0x9E,0xA6,0x21,0xCD,0x9F,0xB4,0xA6,0xF0,0xCD,0x9D,0x9E,0xA6, +0xA0,0x9B,0x21,0xCD,0x9F,0xB4,0xA6,0x20,0xCD,0x9D,0x9E,0xA6,0x21,0xCD,0x9F,0xB4,0x4F,0xCD,0x9D,0x9E,0x3F,0x0C,0x20,0x13,0x5F,0x41,0x72,0xBB,0x00,0x08,0xBF,0x0A,0xCD,0x9D, +0xC0,0x9B,0xFE,0x92,0xC7,0x0A,0xB6,0x0C,0x4C,0xB7,0x0C,0xB6,0x0C,0xA1,0x08,0x25,0xE7,0x32,0x00,0x0C,0xCC,0x9A,0x7A,0xCD,0x9C,0x06,0xA6,0x01,0xCD,0x9B,0xF9,0xCD,0x9F,0x6C, +0xE0,0x9B,0x4F,0xCD,0x9B,0xF9,0xCD,0x9F,0x6C,0xA6,0x4B,0xCD,0x9F,0xB4,0xCC,0x9C,0x17,0x72,0x05,0x50,0x01,0x03,0xA6,0x01,0x81,0x4F,0x81,0x4D,0x27,0x05,0x72,0x14,0x50,0x00, +0x00,0x9C,0x81,0x72,0x15,0x50,0x00,0x81,0x72,0x14,0x50,0x02,0x72,0x14,0x50,0x03,0x72,0x15,0x50,0x04,0x72,0x15,0x50,0x00,0x81,0x72,0x15,0x50,0x02,0x72,0x15,0x50,0x03,0x72, +0x20,0x9C,0x15,0x50,0x04,0x81,0xC6,0x50,0x05,0xAA,0x30,0xC7,0x50,0x05,0xC6,0x50,0x07,0xAA,0x30,0xC7,0x50,0x07,0xC6,0x50,0x08,0xA4,0xCF,0xC7,0x50,0x08,0xC6,0x50,0x09,0xAA, +0x40,0x9C,0x30,0xC7,0x50,0x09,0x35,0x08,0x52,0x12,0x35,0x28,0x52,0x1B,0x72,0x5F,0x52,0x1C,0x35,0x09,0x52,0x1D,0x35,0xA2,0x52,0x13,0x72,0x1C,0x52,0x14,0x35,0x01,0x52,0x1A, +0x60,0x9C,0x72,0x14,0x52,0x11,0x72,0x10,0x52,0x10,0x81,0x88,0x89,0x5E,0xB6,0x01,0x42,0x89,0x1E,0x03,0xB6,0x00,0x42,0x72,0xFB,0x01,0x4F,0x02,0x89,0x1E,0x05,0xB6,0x01,0x42, +0x80,0x9C,0x72,0xFB,0x01,0x5B,0x06,0x84,0x81,0x5D,0x2B,0x06,0x90,0x5D,0x2B,0x0A,0x65,0x81,0x50,0x90,0x5D,0x2B,0x07,0x65,0x50,0x81,0x90,0x50,0x20,0xF9,0x90,0x50,0x20,0xEE, +0xA0,0x9C,0x4D,0x27,0x04,0x58,0x4A,0x26,0xFC,0x81,0x58,0x58,0x58,0x58,0x58,0x81,0x3B,0x00,0x08,0xB7,0x08,0xCD,0x9E,0x85,0xB6,0x08,0xCA,0x54,0x00,0xC7,0x54,0x00,0xA6,0x32, +0xC0,0x9C,0xB7,0x00,0x4A,0x3D,0x00,0x26,0xF9,0x72,0x10,0x54,0x01,0x72,0x0F,0x54,0x00,0xFB,0x72,0x1F,0x54,0x00,0xC6,0x54,0x05,0xB7,0x00,0xC6,0x54,0x04,0x5F,0x97,0x4F,0x02, +0xE0,0x9C,0x90,0x5F,0xB6,0x00,0x61,0xBF,0x00,0x93,0x02,0xBA,0x00,0x01,0x32,0x00,0x08,0x81,0x3B,0x00,0x08,0xB7,0x08,0xCD,0x9C,0x06,0xA6,0x01,0xCD,0x9B,0xF9,0x9D,0x4F,0xCD, +0x00,0x9D,0x9B,0xF9,0x3D,0x08,0x27,0x11,0xA6,0x03,0xCD,0x9F,0xB4,0xA6,0x01,0xCD,0x9B,0xF9,0xA6,0x21,0xCD,0x9F,0xB4,0x20,0x0F,0xA6,0x21,0xCD,0x9F,0xB4,0xA6,0x01,0xCD,0x9B, +0x20,0x9D,0xF9,0xA6,0x03,0xCD,0x9F,0xB4,0xA6,0x03,0xCD,0x9F,0xB4,0x32,0x00,0x08,0x81,0xCD,0x9A,0x2A,0x3B,0x00,0x0C,0xBF,0x08,0xCD,0x9B,0xD5,0xCD,0x9D,0x67,0x9D,0x9D,0xA6, +0x40,0x9D,0x33,0xCD,0x9D,0x9E,0x3F,0x0C,0x20,0x13,0x5F,0x41,0x72,0xBB,0x00,0x08,0xBF,0x0A,0xCD,0x9D,0xFE,0x92,0xC7,0x0A,0xB6,0x0C,0x4C,0xB7,0x0C,0xB6,0x0C,0xA1,0x08,0x25, +0x60,0x9D,0xE7,0x32,0x00,0x0C,0xCC,0x9A,0x7A,0xCD,0x99,0xED,0x3B,0x00,0x0A,0xAE,0x01,0x2C,0xBF,0x08,0x3F,0x0A,0x20,0x04,0x35,0x01,0x00,0x0A,0xBE,0x08,0xA3,0x00,0x01,0x2F, +0x80,0x9D,0x12,0x3D,0x0A,0x26,0x0E,0xCD,0x9B,0xEF,0xA1,0x00,0x27,0xEA,0xBE,0x08,0x5A,0xBF,0x08,0x20,0xE7,0xCD,0x9F,0x6C,0xB6,0x0A,0x32,0x00,0x0A,0xCC,0x9A,0x85,0xCD,0x99, +0xA0,0x9D,0xED,0x3B,0x00,0x0A,0xB7,0x0A,0x35,0x01,0x00,0x09,0x3F,0x08,0x20,0x11,0xB6,0x09,0xB4,0x0A,0xCD,0x9C,0xF0,0xB6,0x09,0x48,0xB7,0x09,0xB6,0x08,0x4C,0xB7,0x08,0xB6, +0xC0,0x9D,0x08,0xA1,0x08,0x25,0xE9,0xA6,0x37,0xCD,0x9F,0xB4,0x32,0x00,0x0A,0xCC,0x9A,0x85,0x3B,0x00,0x08,0xCD,0x9C,0x17,0x9D,0x9D,0x9D,0xCD,0x9C,0x06,0x4F,0xCD,0x9B,0xF9, +0xE0,0x9D,0x9D,0xCD,0x9C,0x17,0xA6,0x03,0xCD,0x9F,0xB4,0x9D,0x9D,0xCD,0x9B,0xEF,0xB7,0x08,0xA6,0x21,0xCD,0x9F,0xB4,0xCD,0x9C,0x17,0xB6,0x08,0x32,0x00,0x08,0x81,0xCD,0x99, +0x00,0x9E,0xED,0x3F,0x09,0x3F,0x08,0x20,0x14,0xCD,0x9D,0xD0,0x5F,0x97,0xB6,0x08,0xCD,0x9C,0xA0,0x9F,0xBA,0x09,0xB7,0x09,0xB6,0x08,0x4C,0xB7,0x08,0xB6,0x08,0xA1,0x08,0x25, +0x20,0x9E,0xE6,0xB6,0x09,0xCC,0x9A,0x85,0x89,0xFE,0x90,0x93,0x85,0x5C,0x5C,0x90,0x5D,0x27,0x1C,0x89,0xFE,0xBF,0x00,0x85,0x5C,0x5C,0x89,0xFE,0xBF,0x02,0x85,0x5C,0x5C,0x51, +0x40,0x9E,0x5A,0x92,0xD6,0x00,0x92,0xD7,0x02,0x5A,0x2A,0xF7,0x51,0x20,0xD9,0x81,0x72,0x15,0x50,0x11,0x72,0x15,0x50,0x12,0x72,0x15,0x50,0x13,0x72,0x17,0x50,0x11,0x72,0x17, +0x60,0x9E,0x50,0x12,0x72,0x17,0x50,0x13,0x72,0x19,0x50,0x0C,0x72,0x19,0x50,0x0D,0x72,0x19,0x50,0x0E,0x81,0x9E,0xC2,0x00,0x21,0x00,0x12,0x00,0x00,0x9E,0x26,0x00,0x02,0x9F, +0x80,0x9E,0xDC,0x00,0x10,0x00,0x00,0x72,0x5F,0x54,0x00,0xC6,0x54,0x01,0xA4,0x8F,0xC7,0x54,0x01,0xC6,0x54,0x01,0xC7,0x54,0x01,0x72,0x13,0x54,0x01,0x72,0x16,0x54,0x02,0x72, +0xA0,0x9E,0x10,0x54,0x01,0x81,0xCD,0x99,0xED,0xB7,0x09,0x3F,0x08,0x4F,0xB7,0x08,0xB6,0x08,0xB1,0x09,0x24,0x0B,0xCD,0x9F,0x78,0xB6,0x08,0xAB,0x01,0xB7,0x08,0x20,0xEF,0xCC, +0xC0,0x9E,0x9A,0x85,0x89,0xFE,0x90,0x93,0x85,0x5C,0x5C,0x90,0x5D,0x27,0x12,0x89,0xFE,0xBF,0x00,0x85,0x5C,0x5C,0x51,0x5A,0x92,0x6F,0x00,0x5A,0x2A,0xFA,0x51,0x20,0xE3,0x81, +0xE0,0x9E,0x90,0xAE,0x9E,0x73,0x20,0x0A,0x93,0x1C,0x00,0x02,0x90,0xFE,0x90,0xFD,0x90,0x93,0x90,0xA3,0x9E,0x85,0x26,0xF0,0x81,0x35,0xCC,0x50,0xE0,0x35,0x55,0x50,0xE0,0x35, +0x00,0x9F,0x06,0x50,0xE1,0x35,0xFF,0x50,0xE2,0x35,0xAA,0x50,0xE0,0x81,0xCD,0x99,0xED,0x52,0x02,0xBF,0x08,0xBE,0x08,0x1F,0x01,0x96,0x5C,0xA6,0x01,0xCD,0x9F,0xDB,0x20,0xF3, +0x20,0x9F,0xAE,0x03,0xFF,0x94,0xCD,0x9F,0xD2,0x5D,0x27,0x03,0xCD,0x9E,0xE0,0xCD,0x97,0x07,0xCC,0x9F,0xD5,0xCD,0x9F,0xBC,0xCD,0x8E,0xFD,0xCD,0x93,0x7D,0xCD,0x9E,0x4E,0xCD, +0x40,0x9F,0x9C,0x24,0xCC,0x9E,0xF7,0xA6,0x07,0xB7,0x00,0xB6,0x00,0xAB,0xFF,0x3D,0x00,0x26,0xF6,0x81,0xA6,0x1A,0xB7,0x00,0xB6,0x00,0xAB,0xFF,0x3D,0x00,0x26,0xF6,0x81,0x88, +0x60,0x9F,0x90,0xF6,0xF7,0x5C,0x90,0x5C,0x0A,0x01,0x26,0xF6,0x84,0x81,0xAE,0x00,0xFA,0x90,0x93,0x93,0x5A,0x90,0x5D,0x26,0xF8,0x81,0xAE,0x01,0xF4,0x90,0x93,0x93,0x5A,0x90, +0x80,0x9F,0x5D,0x26,0xF8,0x81,0xCD,0x9B,0x15,0xA1,0x01,0x26,0x03,0xA6,0x01,0x81,0x4F,0x81,0x1E,0x03,0x1C,0x00,0x04,0x1F,0x03,0x1D,0x00,0x04,0x81,0x35,0x56,0x50,0x62,0x35, +0xA0,0x9F,0xAE,0x50,0x62,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xB7,0x00,0x4A,0x3D,0x00,0x26,0xF9,0x81,0x35,0x02,0x50,0xC6, +0xC0,0x9F,0x81,0x35,0xAA,0x50,0xE0,0x81,0x89,0x85,0xCD,0x9F,0x0C,0x00,0x00,0x00,0x00,0xCC,0x9F,0xD8,0x5F,0x5C,0x81,0xCC,0x9F,0xC6,0x9D,0x20,0xFD,0x81,0x02,0x01,0x00,0x00, +0xE0,0x9F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x34,0x55,0x34, +}; +#endif diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index a546621d0837..0669e4adca63 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -22,6 +22,7 @@ #include #include "fg-core.h" #include "fg-reg.h" +#include #define FG_GEN3_DEV_NAME "qcom,fg-gen3" @@ -400,6 +401,26 @@ module_param_named( profile_dump, fg_profile_dump, bool, S_IRUSR | S_IWUSR ); +static struct external_battery_gauge *external_fg; + +void external_battery_gauge_register(struct external_battery_gauge *batt_gauge) +{ + if (external_fg) { + external_fg = batt_gauge; + pr_err("qpnp-charger %s multiple battery gauge called\n", + __func__); + } else { + external_fg = batt_gauge; + } +} +EXPORT_SYMBOL(external_battery_gauge_register); + +void external_battery_gauge_unregister( + struct external_battery_gauge *batt_gauge) +{ + external_fg = NULL; +} +EXPORT_SYMBOL(external_battery_gauge_unregister); static int fg_sram_dump_period_ms = 20000; module_param_named( sram_dump_period_ms, fg_sram_dump_period_ms, int, S_IRUSR | S_IWUSR @@ -943,6 +964,7 @@ static int fg_batt_missing_config(struct fg_chip *chip, bool enable) return rc; } +#define OP_SW_DEFAULT_ID 200000 static int fg_get_batt_id(struct fg_chip *chip) { int rc, ret, batt_id = 0; @@ -966,7 +988,7 @@ static int fg_get_batt_id(struct fg_chip *chip) msleep(200); fg_dbg(chip, FG_STATUS, "batt_id: %d\n", batt_id); - chip->batt_id_ohms = batt_id; + chip->batt_id_ohms = OP_SW_DEFAULT_ID; out: ret = fg_batt_missing_config(chip, true); if (ret < 0) { @@ -1136,6 +1158,7 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles_init, /* Other functions HERE */ +/*set Ibat 500mA by default */ static void fg_notify_charger(struct fg_chip *chip) { union power_supply_propval prop = {0, }; @@ -1147,15 +1170,6 @@ static void fg_notify_charger(struct fg_chip *chip) if (!chip->profile_available) return; - prop.intval = chip->bp.float_volt_uv; - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); - if (rc < 0) { - pr_err("Error in setting voltage_max property on batt_psy, rc=%d\n", - rc); - return; - } - prop.intval = chip->bp.fastchg_curr_ma * 1000; rc = power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop); @@ -1165,6 +1179,14 @@ static void fg_notify_charger(struct fg_chip *chip) return; } + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_NOTIFY_CHARGER_SET_PARAMETER, &prop); + if (rc < 0) { + pr_err("Error in setting voltage_max property on batt_psy, rc=%d\n", + rc); + return; + } + fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n"); } @@ -3526,6 +3548,8 @@ out: return rc; } +/* Add dash charging */ +#define DEFALUT_BATT_TEMP 250 static int fg_esr_validate(struct fg_chip *chip) { int rc, esr_uohms; @@ -3737,22 +3761,41 @@ static int fg_psy_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CAPACITY: - rc = fg_get_prop_capacity(chip, &pval->intval); +/* Add dash charging */ + if (!get_extern_fg_regist_done()) + pval->intval = get_prop_pre_shutdown_soc(); + else if (chip->use_external_fg && external_fg + && external_fg->get_battery_soc) + pval->intval = external_fg->get_battery_soc(); + else + pval->intval = 50; break; case POWER_SUPPLY_PROP_CAPACITY_RAW: rc = fg_get_msoc_raw(chip, &pval->intval); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - if (chip->battery_missing) - pval->intval = 3700000; + if (chip->use_external_fg && external_fg + && external_fg->get_battery_mvolts) + pval->intval = external_fg->get_battery_mvolts(); else - rc = fg_get_battery_voltage(chip, &pval->intval); + pval->intval = 4000000; /* 4000mV */ break; case POWER_SUPPLY_PROP_CURRENT_NOW: - rc = fg_get_battery_current(chip, &pval->intval); + if (chip->use_external_fg && external_fg + && external_fg->get_average_current) + pval->intval = external_fg->get_average_current(); + else + pval->intval = 0; break; case POWER_SUPPLY_PROP_TEMP: - rc = fg_get_battery_temp(chip, &pval->intval); + if (!get_extern_fg_regist_done() + && get_extern_bq_present()) + pval->intval = DEFALUT_BATT_TEMP; + else if (chip->use_external_fg && external_fg + && external_fg->get_average_current){ + pval->intval = external_fg->get_battery_temperature(); + } else + pval->intval = -400; break; case POWER_SUPPLY_PROP_COLD_TEMP: rc = fg_get_jeita_threshold(chip, JEITA_COLD, &pval->intval); @@ -3833,6 +3876,34 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_DEBUG_BATTERY: pval->intval = is_debug_batt_id(chip); break; + case POWER_SUPPLY_PROP_FG_CAPACITY: + rc = fg_get_prop_capacity(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_FG_VOLTAGE_NOW: + if (chip->battery_missing) + pval->intval = 3700000; + else + rc = fg_get_battery_voltage(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_FG_CURRENT_NOW: + rc = fg_get_battery_current(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_BATTERY_HEALTH: + if (chip->use_external_fg && external_fg + && external_fg->get_batt_health) + pval->intval = external_fg->get_batt_health(); + else if (get_extern_fg_regist_done() == false) + pval->intval = -1; + else + pval->intval = -1; + break; + case POWER_SUPPLY_PROP_BQ_SOC: + if (chip->use_external_fg && external_fg + && external_fg->get_batt_bq_soc) + pval->intval = external_fg->get_batt_bq_soc(); + else + pval->intval = 50; + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: rc = fg_get_sram_prop(chip, FG_SRAM_VBATT_FULL, &pval->intval); break; @@ -3862,6 +3933,12 @@ static int fg_psy_get_property(struct power_supply *psy, return 0; } +static void oem_update_cc_cv_setpoint( + struct fg_chip *chip, int cv_float_point); +static void oneplus_set_allow_read_iic( + struct fg_chip *chip, bool status); +static void oneplus_set_lcd_off_status( + struct fg_chip *chip, bool status); static int fg_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *pval) @@ -3870,6 +3947,16 @@ static int fg_psy_set_property(struct power_supply *psy, int rc = 0; switch (psp) { + + case POWER_SUPPLY_PROP_CC_TO_CV_POINT: + oem_update_cc_cv_setpoint(chip, pval->intval); + break; + case POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC: + oneplus_set_allow_read_iic(chip, pval->intval); + break; + case POWER_SUPPLY_PROP_UPDATE_LCD_IS_OFF: + oneplus_set_lcd_off_status(chip, pval->intval); + break; case POWER_SUPPLY_PROP_CYCLE_COUNT_ID: if ((pval->intval > 0) && (pval->intval <= BUCKET_COUNT)) { chip->cyc_ctr.id = pval->intval; @@ -3970,6 +4057,7 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: case POWER_SUPPLY_PROP_HOT_TEMP: + case POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC: return 1; default: break; @@ -4047,6 +4135,10 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CC_STEP, POWER_SUPPLY_PROP_CC_STEP_SEL, + POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC, + POWER_SUPPLY_PROP_BQ_SOC, + POWER_SUPPLY_PROP_BATTERY_HEALTH, + }; static const struct power_supply_desc fg_psy_desc = { @@ -4307,6 +4399,11 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + rc = fg_masked_write(chip, BATT_INFO_ESR_PULL_DN_CFG(chip), 0xFF, 0); + if (rc < 0) { + pr_err("Error in writing ESR PULL DN, rc=%d\n", rc); + return rc; + } fg_encode(chip->sp, FG_SRAM_ESR_PULSE_THRESH, chip->dt.esr_pulse_thresh_ma, buf); rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR_PULSE_THRESH].addr_word, @@ -4325,6 +4422,11 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } + rc = fg_masked_write(chip, BATT_INFO_ESR_PULL_DN_CFG(chip), 0xFF, 0); + if (rc < 0) { + pr_err("Error in writing ESR PULL DN, rc=%d\n", rc); + return rc; + } if (is_debug_batt_id(chip)) { val = ESR_NO_PULL_DOWN; rc = fg_masked_write(chip, BATT_INFO_ESR_PULL_DN_CFG(chip), @@ -4620,7 +4722,6 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { [MSOC_DELTA_IRQ] = { .name = "msoc-delta", .handler = fg_delta_msoc_irq_handler, - .wakeable = true, }, [BSOC_DELTA_IRQ] = { .name = "bsoc-delta", @@ -4640,7 +4741,6 @@ static struct fg_irq_info fg_irqs[FG_IRQ_MAX] = { [BATT_TEMP_DELTA_IRQ] = { .name = "batt-temp-delta", .handler = fg_delta_batt_temp_irq_handler, - .wakeable = true, }, [BATT_MISSING_IRQ] = { .name = "batt-missing", @@ -5031,6 +5131,10 @@ static int fg_parse_dt(struct fg_chip *chip) else chip->dt.rsense_sel = (u8)temp & SOURCE_SELECT_MASK; + chip->use_external_fg = + of_property_read_bool(node, "oem,use_external_fg"); + pr_info("use_external_fg=%d\n", chip->use_external_fg); + chip->dt.jeita_thresholds[JEITA_COLD] = DEFAULT_BATT_TEMP_COLD; chip->dt.jeita_thresholds[JEITA_COOL] = DEFAULT_BATT_TEMP_COOL; chip->dt.jeita_thresholds[JEITA_WARM] = DEFAULT_BATT_TEMP_WARM; @@ -5240,6 +5344,8 @@ static int fg_parse_dt(struct fg_chip *chip) static void fg_cleanup(struct fg_chip *chip) { + if (chip->fg_psy) + power_supply_unregister(chip->fg_psy); alarm_try_to_cancel(&chip->esr_filter_alarm); power_supply_unreg_notifier(&chip->nb); debugfs_remove_recursive(chip->dfs_root); @@ -5258,6 +5364,29 @@ static void fg_cleanup(struct fg_chip *chip) dev_set_drvdata(chip->dev, NULL); } +static void oem_update_cc_cv_setpoint( +struct fg_chip *chip, int cv_float_point) +{ + /* TODO: write CC_CV_SETPOINT_REG */ +} + +static void oneplus_set_allow_read_iic(struct fg_chip *chip, bool status) +{ + if (chip->use_external_fg && external_fg + && external_fg->set_allow_reading) + external_fg->set_allow_reading(status); + else + pr_info("set allow read extern fg iic fail\n"); +} + +static void oneplus_set_lcd_off_status(struct fg_chip *chip, bool status) +{ + if (chip->use_external_fg && external_fg + && external_fg->set_lcd_off_status) + external_fg->set_lcd_off_status(status); + else + pr_info("set lcd off status fail\n"); +} static int fg_gen3_probe(struct platform_device *pdev) { struct fg_chip *chip; diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index a7c206c42418..26c66f42d1ad 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -9,6 +9,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#define pr_fmt(fmt) "SMB2: %s: " fmt, __func__ #include #include @@ -24,6 +25,8 @@ #include #include #include +#include +#include #include "smb-reg.h" #include "smb-lib.h" #include "storm-watch.h" @@ -179,7 +182,36 @@ struct smb2 { bool bad_part; }; +static int smbchg_cutoff_volt_with_charger = 3240; +struct smb_charger *g_chip; +module_param_named( + cutoff_volt_with_charger, + smbchg_cutoff_volt_with_charger, + int, S_IRUSR | S_IWUSR); + +#define OF_PROP_READ(node, dt_property, prop, retval, optional) \ +do { \ + if (retval) \ + break; \ + if (optional) \ + prop = -EINVAL; \ + \ + retval = of_property_read_u32(node, \ + dt_property, \ + &prop); \ + \ + if ((retval == -EINVAL) && optional) \ + retval = 0; \ + else if (retval) \ + pr_err("Error reading " #dt_property \ + " property rc = %d\n", rc); \ +} while (0) + +#ifdef CONFIG_OP_DEBUG_CHG +static int __debug_mask = PR_OP_DEBUG; +#else static int __debug_mask; +#endif module_param_named( debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR ); @@ -212,9 +244,107 @@ static int smb2_parse_dt(struct smb2 *chip) return -EINVAL; } - chg->step_chg_enabled = of_property_read_bool(node, - "qcom,step-charging-enable"); + /* read ibatmax setting for different temp regions */ + OF_PROP_READ(node, "ibatmax-little-cold-ma", + chg->ibatmax[BATT_TEMP_LITTLE_COLD], rc, 1); + OF_PROP_READ(node, "ibatmax-cool-ma", + chg->ibatmax[BATT_TEMP_COOL], rc, 1); + OF_PROP_READ(node, "ibatmax-little-cool-ma", + chg->ibatmax[BATT_TEMP_LITTLE_COOL], rc, 1); + OF_PROP_READ(node, "ibatmax-pre-normal-ma", + chg->ibatmax[BATT_TEMP_PRE_NORMAL], rc, 1); + OF_PROP_READ(node, "ibatmax-normal-ma", + chg->ibatmax[BATT_TEMP_NORMAL], rc, 1); + OF_PROP_READ(node, "ibatmax-warm-ma", + chg->ibatmax[BATT_TEMP_WARM], rc, 1); + /* read vbatmax setting for different temp regions */ + OF_PROP_READ(node, "vbatmax-little-cold-mv", + chg->vbatmax[BATT_TEMP_LITTLE_COLD], rc, 1); + OF_PROP_READ(node, "vbatmax-cool-mv", + chg->vbatmax[BATT_TEMP_COOL], rc, 1); + OF_PROP_READ(node, "vbatmax-little-cool-mv", + chg->vbatmax[BATT_TEMP_LITTLE_COOL], rc, 1); + OF_PROP_READ(node, "vbatmax-pre-normal-mv", + chg->vbatmax[BATT_TEMP_PRE_NORMAL], rc, 1); + OF_PROP_READ(node, "vbatmax-normal-mv", + chg->vbatmax[BATT_TEMP_NORMAL], rc, 1); + OF_PROP_READ(node, "vbatmax-warm-mv", + chg->vbatmax[BATT_TEMP_WARM], rc, 1); + + /* read vbatdet setting for different temp regions */ + OF_PROP_READ(node, "vbatdet-little-cold-mv", + chg->vbatdet[BATT_TEMP_LITTLE_COLD], rc, 1); + OF_PROP_READ(node, "vbatdet-cool-mv", + chg->vbatdet[BATT_TEMP_COOL], rc, 1); + OF_PROP_READ(node, "vbatdet-little-cool-mv", + chg->vbatdet[BATT_TEMP_LITTLE_COOL], rc, 1); + OF_PROP_READ(node, "vbatdet-pre-normal-mv", + chg->vbatdet[BATT_TEMP_PRE_NORMAL], rc, 1); + OF_PROP_READ(node, "vbatdet-normal-mv", + chg->vbatdet[BATT_TEMP_NORMAL], rc, 1); + OF_PROP_READ(node, "vbatdet-warm-mv", + chg->vbatdet[BATT_TEMP_WARM], rc, 1); + + /* read temp region settings */ + OF_PROP_READ(node, "cold-bat-decidegc", + chg->BATT_TEMP_T0, rc, 1); + chg->BATT_TEMP_T0 = 0 - chg->BATT_TEMP_T0; + OF_PROP_READ(node, "little-cold-bat-decidegc", + chg->BATT_TEMP_T1, rc, 1); + OF_PROP_READ(node, "cool-bat-decidegc", + chg->BATT_TEMP_T2, rc, 1); + OF_PROP_READ(node, "little-cool-bat-decidegc", + chg->BATT_TEMP_T3, rc, 1); + OF_PROP_READ(node, "pre-normal-bat-decidegc", + chg->BATT_TEMP_T4, rc, 1); + OF_PROP_READ(node, "warm-bat-decidegc", + chg->BATT_TEMP_T5, rc, 1); + OF_PROP_READ(node, "hot-bat-decidegc", + chg->BATT_TEMP_T6, rc, 1); + + /* read other settings */ + OF_PROP_READ(node, "qcom,cutoff-voltage-with-charger", + smbchg_cutoff_volt_with_charger, rc, 1); + + chg->chg_enabled = !(of_property_read_bool(node, + "qcom,charging-disabled")); + + chg->pd_disabled = of_property_read_bool(node, + "disable-pd"); + pr_info("T0=%d, T1=%d, T2=%d, T3=%d, T4=%d, T5=%d, T6=%d\n", + chg->BATT_TEMP_T0, chg->BATT_TEMP_T1, chg->BATT_TEMP_T2, + chg->BATT_TEMP_T3, chg->BATT_TEMP_T4, chg->BATT_TEMP_T5, + chg->BATT_TEMP_T6); + pr_info("BATT_TEMP_LITTLE_COLD=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_LITTLE_COLD], + chg->vbatmax[BATT_TEMP_LITTLE_COLD], + chg->vbatdet[BATT_TEMP_LITTLE_COLD]); + pr_info("BATT_TEMP_COOL=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_COOL], + chg->vbatmax[BATT_TEMP_COOL], + chg->vbatdet[BATT_TEMP_COOL]); + pr_info("BATT_TEMP_LITTLE_COOL=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_LITTLE_COOL], + chg->vbatmax[BATT_TEMP_LITTLE_COOL], + chg->vbatdet[BATT_TEMP_LITTLE_COOL]); + pr_info("BATT_TEMP_PRE_NORMAL=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_PRE_NORMAL], + chg->vbatmax[BATT_TEMP_PRE_NORMAL], + chg->vbatdet[BATT_TEMP_PRE_NORMAL]); + pr_info("BATT_TEMP_NORMAL=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_NORMAL], + chg->vbatmax[BATT_TEMP_NORMAL], + chg->vbatdet[BATT_TEMP_NORMAL]); + pr_info("BATT_TEMP_WARM=%d, %d, %d\n", + chg->ibatmax[BATT_TEMP_WARM], + chg->vbatmax[BATT_TEMP_WARM], + chg->vbatdet[BATT_TEMP_WARM]); + pr_info("cutoff_volt_with_charger=%d, disable-pd=%d\n", + smbchg_cutoff_volt_with_charger, chg->pd_disabled); + + /* disable step_chg */ + chg->step_chg_enabled = false; chg->sw_jeita_enabled = of_property_read_bool(node, "qcom,sw-jeita-enable"); @@ -346,6 +476,8 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_TYPEC_MODE, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, + POWER_SUPPLY_PROP_OTG_SWITCH, + POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, @@ -419,6 +551,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, else val->intval = chg->typec_mode; break; + case POWER_SUPPLY_PROP_OTG_SWITCH: + val->intval = chg->otg_switch; + break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: if (chg->micro_usb_mode) val->intval = POWER_SUPPLY_TYPEC_PR_NONE; @@ -426,6 +561,7 @@ static int smb2_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_typec_power_role(chg, val); break; case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION: + case POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION: if (chg->micro_usb_mode) val->intval = 0; else @@ -495,7 +631,8 @@ static int smb2_usb_set_prop(struct power_supply *psy, int rc = 0; mutex_lock(&chg->lock); - if (!chg->typec_present) { + + if (!chg->typec_present && psp != POWER_SUPPLY_PROP_OTG_SWITCH) { rc = -EINVAL; goto unlock; } @@ -504,6 +641,9 @@ static int smb2_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_CURRENT_MAX: rc = smblib_set_prop_pd_current_max(chg, val); break; + case POWER_SUPPLY_PROP_OTG_SWITCH: + rc = op_set_prop_otg_switch(chg, val); + break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; @@ -550,6 +690,7 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { + case POWER_SUPPLY_PROP_OTG_SWITCH: case POWER_SUPPLY_PROP_CTM_CURRENT_MAX: return 1; default: @@ -923,6 +1064,14 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHG_PROTECT_STATUS, + POWER_SUPPLY_PROP_FASTCHG_STATUS, + POWER_SUPPLY_PROP_FASTCHG_STARTING, + POWER_SUPPLY_CUTOFF_VOLT_WITH_CHARGER, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, + POWER_SUPPLY_PROP_IS_AGING_TEST, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, @@ -959,7 +1108,7 @@ static int smb2_batt_get_prop(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: - rc = smblib_get_prop_batt_status(chg, val); + val->intval = get_prop_batt_status(chg); break; case POWER_SUPPLY_PROP_HEALTH: rc = smblib_get_prop_batt_health(chg, val); @@ -976,6 +1125,30 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_get_prop_batt_capacity(chg, val); break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + rc = smblib_get_prop_usb_voltage_now(chg, val); + break; + case POWER_SUPPLY_PROP_CHG_PROTECT_STATUS: + val->intval = get_prop_chg_protect_status(chg); + break; + case POWER_SUPPLY_PROP_FASTCHG_STATUS: + val->intval = get_prop_fastchg_status(chg); + break; + case POWER_SUPPLY_CUTOFF_VOLT_WITH_CHARGER: + val->intval = smbchg_cutoff_volt_with_charger; + break; + case POWER_SUPPLY_PROP_FASTCHG_STARTING: + val->intval = op_get_fastchg_ing(chg); + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + val->intval = chg->chg_enabled; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: + rc = smblib_get_prop_input_current_limited(chg, val); + break; + case POWER_SUPPLY_PROP_IS_AGING_TEST: + val->intval = chg->is_aging_test; + break; case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: rc = smblib_get_prop_system_temp_level(chg, val); break; @@ -1081,6 +1254,49 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: rc = smblib_set_prop_system_temp_level(chg, val); break; + case POWER_SUPPLY_PROP_CHECK_USB_UNPLUG: + if (chg->vbus_present && !chg->dash_present) + update_dash_unplug_status(); + break; + case POWER_SUPPLY_PROP_SWITCH_DASH: + rc = check_allow_switch_dash(chg, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: + pr_info("set iusb %d mA\n", val->intval); + if (__debug_mask == PR_OP_DEBUG + || val->intval == 900000) + op_usb_icl_set(chg, val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + rc = smblib_set_prop_chg_voltage(chg, val); + break; + case POWER_SUPPLY_PROP_TEMP: + rc = smblib_set_prop_batt_temp(chg, val); + break; + case POWER_SUPPLY_PROP_CHG_PROTECT_STATUS: + rc = smblib_set_prop_chg_protect_status(chg, val); + break; + case POWER_SUPPLY_PROP_NOTIFY_CHARGER_SET_PARAMETER: + rc = smblib_set_prop_charge_parameter_set(chg); + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + if (!val->intval) { + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + switch_mode_to_normal(); + op_set_fast_chg_allow(chg, false); + } + } + rc = vote(chg->usb_icl_votable, USER_VOTER, + !val->intval, 0); + rc = vote(chg->dc_suspend_votable, USER_VOTER, + !val->intval, 0); + chg->chg_enabled = (bool)val->intval; + break; + case POWER_SUPPLY_PROP_IS_AGING_TEST: + chg->is_aging_test = (bool)val->intval; + __debug_mask = PR_OP_DEBUG; + break; case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_set_prop_batt_capacity(chg, val); break; @@ -1162,6 +1378,12 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CHG_PROTECT_STATUS: + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: + case POWER_SUPPLY_PROP_IS_AGING_TEST: case POWER_SUPPLY_PROP_PARALLEL_DISABLE: case POWER_SUPPLY_PROP_DP_DM: case POWER_SUPPLY_PROP_RERUN_AICL: @@ -1365,6 +1587,32 @@ static int smb2_configure_typec(struct smb_charger *chg) "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } + if (chg->otg_switch) { + /* restore it back to 0xA5 */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5); + if (rc < 0) + dev_err(chg->dev, + "Couldn't restore it back rc=%d\n", rc); + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, 0); + } else { + /* disable PBS workaround when forcing sink mode */ + rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0); + if (rc < 0) + dev_err(chg->dev, + "Couldn't disable PBS workaround rc=%d\n", rc); + + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, + UFP_EN_CMD_BIT); + } + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure power role for DRP rc=%d\n", rc); + return rc; + } /* * disable Type-C factory mode and stay in Attached.SRC state when VCONN @@ -1487,7 +1735,9 @@ static int smb2_init_hw(struct smb2 *chip) &chg->default_icl_ua); if (chip->dt.usb_icl_ua < 0) chip->dt.usb_icl_ua = chg->default_icl_ua; - + pr_info("vbat_max=%d, ibat_max=%d, iusb_max=%d\n", + chg->batt_profile_fv_uv, + chg->batt_profile_fcc_ua, chip->dt.usb_icl_ua); if (chip->dt.dc_icl_ua < 0) smblib_get_charge_param(chg, &chg->param.dc_icl, &chip->dt.dc_icl_ua); @@ -1536,15 +1786,14 @@ static int smb2_init_hw(struct smb2 *chip) } /* votes must be cast before configuring software control */ - /* vote 0mA on usb_icl for non battery platforms */ vote(chg->usb_icl_votable, - DEFAULT_VOTER, chip->dt.no_battery, 0); + DEFAULT_VOTER, !chg->chg_enabled, 0); vote(chg->dc_suspend_votable, - DEFAULT_VOTER, chip->dt.no_battery, 0); - vote(chg->fcc_votable, - BATT_PROFILE_VOTER, true, chg->batt_profile_fcc_ua); - vote(chg->fv_votable, - BATT_PROFILE_VOTER, true, chg->batt_profile_fv_uv); + DEFAULT_VOTER, !chg->chg_enabled, 0); + smblib_set_charge_param(chg, &chg->param.fcc, + chg->ibatmax[BATT_TEMP_NORMAL] * 1000); + smblib_set_charge_param(chg, &chg->param.fv, + chg->vbatmax[BATT_TEMP_NORMAL] * 1000); vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, @@ -1561,6 +1810,22 @@ static int smb2_init_hw(struct smb2 *chip) chg->micro_usb_mode, 0); vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER, chg->micro_usb_mode, 0); + /* disable HVDCP */ + rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, + HVDCP_EN_BIT, 0); + if (rc < 0) + dev_err(chg->dev, "Couldn't disable HVDCP rc=%d\n", rc); + rc = smblib_masked_write(chg, USBIN_SOURCE_CHANGE_INTRPT_ENB_REG, + SLOW_IRQ_EN_CFG_BIT, 0); + if (rc < 0) + dev_err(chg->dev, + "Couldn't clean slow plugin irq=%d\n", rc); + + /* aicl rerun time */ + rc = smblib_masked_write(chg, AICL_RERUN_TIME_CFG_REG, + BIT(0)|BIT(1), 0); + if (rc < 0) + dev_err(chg->dev, "Couldn't set aicl rerunTimerc=%d\n", rc); /* * AICL configuration: @@ -1618,6 +1883,15 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + /*xianglin modify otg current load to 1.5A*/ + rc = smblib_masked_write( + chg, OTG_CURRENT_LIMIT_CFG_REG, + OTG_CURRENT_LIMIT_MASK, 0x5); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure VBUS for SW control rc=%d\n", rc); + return rc; + } val = (ilog2(chip->dt.wd_bark_time / 16) << BARK_WDOG_TIMEOUT_SHIFT) & BARK_WDOG_TIMEOUT_MASK; val |= BITE_WDOG_TIMEOUT_8S; @@ -1868,6 +2142,7 @@ static int smb2_chg_config_init(struct smb2 *chip) return -EINVAL; } + pr_info("PMI8998 Revision=0x%x\n", pmic_rev_id->rev4); return 0; } @@ -2046,7 +2321,7 @@ static struct smb_irq_info smb2_irqs[] = { }, [AICL_DONE_IRQ] = { .name = "aicl-done", - .handler = smblib_handle_debug, + .handler = smblib_handle_aicl_done, }, [HIGH_DUTY_CYCLE_IRQ] = { .name = "high-duty-cycle", @@ -2244,6 +2519,23 @@ static void smb2_create_debugfs(struct smb2 *chip) #endif +#ifdef CONFIG_PROC_FS +static ssize_t write_ship_mode(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + + if (count) { + g_chip->ship_mode = true; + pr_err(" * * * XCB * * * write_ship_mode\n"); + } + return count; +} + +static const struct file_operations proc_ship_mode_operations = { + .write = write_ship_mode, + .llseek = noop_llseek, +}; +#endif static int smb2_probe(struct platform_device *pdev) { struct smb2 *chip; @@ -2257,6 +2549,7 @@ static int smb2_probe(struct platform_device *pdev) return -ENOMEM; chg = &chip->chg; + g_chip = chg; chg->dev = &pdev->dev; chg->param = v1_params; chg->debug_mask = &__debug_mask; @@ -2293,6 +2586,7 @@ static int smb2_probe(struct platform_device *pdev) /* set driver data before resources request it */ platform_set_drvdata(pdev, chip); + op_charge_info_init(chg); rc = smb2_init_vbus_regulator(chip); if (rc < 0) { @@ -2408,6 +2702,18 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } batt_charge_type = val.intval; +#ifdef CONFIG_PROC_FS + if (!proc_create("ship_mode", S_IFREG | S_IWUSR | S_IRUGO, NULL, + &proc_ship_mode_operations)) + pr_err("Failed to register proc interface\n"); +#endif + if (usb_present) { + schedule_delayed_work(&chg->non_standard_charger_check_work, + msecs_to_jiffies(TIME_1000MS)); + chg->boot_usb_present = true; + } + if (!usb_present && chg->vbus_present) + op_handle_usb_plugin(chg); device_init_wakeup(chg->dev, true); @@ -2444,11 +2750,14 @@ static int smb2_remove(struct platform_device *pdev) struct smb2 *chip = platform_get_drvdata(pdev); struct smb_charger *chg = &chip->chg; - power_supply_unregister(chg->batt_psy); - power_supply_unregister(chg->usb_psy); - power_supply_unregister(chg->usb_port_psy); - regulator_unregister(chg->vconn_vreg->rdev); - regulator_unregister(chg->vbus_vreg->rdev); + if (chg->usb_psy) + power_supply_unregister(chg->batt_psy); + if (chg->batt_psy) + power_supply_unregister(chg->batt_psy); + if (chg->vconn_vreg && chg->vconn_vreg->rdev) + regulator_unregister(chg->vconn_vreg->rdev); + if (chg->vbus_vreg && chg->vbus_vreg->rdev) + regulator_unregister(chg->vbus_vreg->rdev); platform_set_drvdata(pdev, NULL); return 0; @@ -2458,6 +2767,18 @@ static void smb2_shutdown(struct platform_device *pdev) { struct smb2 *chip = platform_get_drvdata(pdev); struct smb_charger *chg = &chip->chg; +#ifdef CONFIG_PROC_FS + pr_info("smbchg_shutdown\n"); + if (chg->ship_mode) { + pr_info("smbchg_shutdown enter ship_mode\n"); + smblib_masked_write(chg, SHIP_MODE_REG, SHIP_MODE_EN_BIT, + SHIP_MODE_EN_BIT); + msleep(1000); + pr_err("after 1s\n"); + while (1) + ; + } +#endif /* disable all interrupts */ smb2_disable_interrupts(chg); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 81623c65ea8e..e44634e97bf8 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -9,6 +9,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#define pr_fmt(fmt) "SMBLIB: %s: " fmt, __func__ #include #include @@ -25,6 +26,53 @@ #include "battery.h" #include "step-chg-jeita.h" #include "storm-watch.h" +#include +#include +#include +#include +#include +#if defined(CONFIG_FB) +#include +#include +#endif /*CONFIG_FB*/ + +#define SOC_INVALID 0x7E +#define SOC_DATA_REG_0 0x88D +#define HEARTBEAT_INTERVAL_MS 6000 +#define CHG_TIMEOUT_COUNT 6000 /* 10hr */ +#define CHG_SOFT_OVP_MV 5800 +#define BATT_SOFT_OVP_MV 4500 +#define CHG_SOFT_UVP_MV 4300 +#define CHG_VOLTAGE_NORMAL 5000 +#define BATT_REMOVE_TEMP -400 +#define BATT_TEMP_HYST 20 + +struct smb_charger *g_chg; +struct qpnp_pon *pm_pon; + +static struct external_battery_gauge *fast_charger; +static int op_charging_en(struct smb_charger *chg, bool en); +static bool set_prop_fast_switch_to_normal_false(struct smb_charger *chg); + +static void op_battery_temp_region_set(struct smb_charger *chg, + enum temp_region_type batt_temp_region); +static void set_usb_switch(struct smb_charger *chg, bool enable); +static void op_handle_usb_removal(struct smb_charger *chg); +static bool get_prop_fast_switch_to_normal(struct smb_charger *chg); +static int get_prop_batt_temp(struct smb_charger *chg); +static int get_prop_batt_capacity(struct smb_charger *chg); +static int get_prop_batt_current_now(struct smb_charger *chg); +static int get_prop_batt_voltage_now(struct smb_charger *chg); +static int set_property_on_fg(struct smb_charger *chg, + enum power_supply_property prop, int val); +static int set_dash_charger_present(int status); +static enum temp_region_type + op_battery_temp_region_get(struct smb_charger *chg); +static int get_prop_fg_capacity(struct smb_charger *chg); +static int get_prop_fg_current_now(struct smb_charger *chg); +static int get_prop_fg_voltage_now(struct smb_charger *chg); +static void op_check_charger_collapse(struct smb_charger *chg); +static int op_set_collapse_fet(struct smb_charger *chg, bool on); #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -233,7 +281,7 @@ static const struct apsd_result const smblib_apsd_results[] = { [FLOAT] = { .name = "FLOAT", .bit = FLOAT_CHARGER_BIT, - .pst = POWER_SUPPLY_TYPE_USB_FLOAT + .pst = POWER_SUPPLY_TYPE_USB_DCP }, [HVDCP2] = { .name = "HVDCP2", @@ -259,10 +307,10 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) return result; } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", apsd_stat); - - if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) + if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) { + pr_info("APSD_DTC_STATUS_DONE_BIT is 0\n"); return result; - + } rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read APSD_RESULT_STATUS rc=%d\n", @@ -378,6 +426,7 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; int irq = chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq; + pr_info("suspend=%d\n", suspend); if (suspend && irq) { if (chg->usb_icl_change_irq_enabled) { @@ -567,6 +616,13 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) chg->real_charger_type = apsd_result->pst; } + if (chg->dash_on) { + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + } else { + chg->real_charger_type = apsd_result->pst; + chg->usb_psy_desc.type = apsd_result->pst; + } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n", apsd_result->name, chg->pd_active); return apsd_result; @@ -865,6 +921,43 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } +int op_usb_icl_set(struct smb_charger *chg, int icl_ua) +{ + int rc = 0; + bool override; + pr_info("%s,icl_ua=%d\n", __func__, icl_ua); + disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); + goto enable_icl_changed_interrupt; + } + + /* determine if override needs to be enforced */ + override = true; + /* 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); + 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; +} + static int get_sdp_current(struct smb_charger *chg, int *icl_ua) { @@ -892,6 +985,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; bool override; + pr_info("%s,icl_ua=%d\n", __func__, icl_ua); /* suspend and return if 25mA or less is requested */ if (icl_ua < USBIN_25MA) @@ -903,13 +997,18 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* configure current */ if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { + if (chg->non_std_chg_present) { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, + icl_ua); + override = false; + } else + rc = set_sdp_current(chg, icl_ua); 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 { - set_sdp_current(chg, 100000); rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); @@ -1058,6 +1157,7 @@ static int smblib_awake_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; + pr_info("set awake=%d\n", awake); if (awake) pm_stay_awake(chg->dev); else @@ -1072,6 +1172,7 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, struct smb_charger *chg = data; int rc; + pr_info("set chg_disable=%d\n", chg_disable); rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, CHARGING_ENABLE_CMD_BIT, chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT); @@ -1702,9 +1803,9 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, int smblib_get_prop_batt_health(struct smb_charger *chg, union power_supply_propval *val) { - union power_supply_propval pval; + int rc; - int effective_fv_uv; + u8 stat; rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat); @@ -1717,23 +1818,11 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, stat); if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) { - rc = smblib_get_prop_from_bms(chg, - POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); - if (!rc) { - /* - * If Vbatt is within 40mV above Vfloat, then don't - * treat it as overvoltage. - */ - effective_fv_uv = get_effective_result(chg->fv_votable); - if (pval.intval >= effective_fv_uv + 40000) { - val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n", - pval.intval, effective_fv_uv); - goto done; - } - } + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + } + if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT) val->intval = POWER_SUPPLY_HEALTH_COLD; else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT) @@ -1744,8 +1833,6 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, val->intval = POWER_SUPPLY_HEALTH_WARM; else val->intval = POWER_SUPPLY_HEALTH_GOOD; - -done: return rc; } @@ -1853,6 +1940,96 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } +#define POWER_ROLE_BIT (DFP_EN_CMD_BIT | UFP_EN_CMD_BIT) +static int op_check_battery_temp(struct smb_charger *chg); + +int op_set_prop_otg_switch(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + u8 power_role; + u8 ctrl = 0; + bool pre_otg_switch; + int i = 0; + + pre_otg_switch = chg->otg_switch; + chg->otg_switch = val->intval; + + if (chg->otg_switch == pre_otg_switch) + return rc; + + pr_info("set otg_switch=%d\n", chg->otg_switch); + if (chg->otg_switch) + power_role = 0; + else + power_role = UFP_EN_CMD_BIT; + + for (i = 0; i < 10; i++) { + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, power_role); + if (rc < 0) { + smblib_err(chg, "Couldn't write 0x%02x to 0x1368 rc=%d\n", + power_role, rc); + return rc; + } + usleep_range(30000, 31000); + ctrl = 0; + rc = smblib_read(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl); + if (rc < 0) { + smblib_err(chg, "Couldn't read err=%d\n", rc); + return rc; + } + if ((power_role == 0) && ((ctrl & POWER_ROLE_BIT) == 0)) + break; + if ((power_role == UFP_EN_CMD_BIT) && (ctrl | UFP_EN_CMD_BIT)) + break; + } + pr_info("retry time = %d,ctrl = %d\n", i, ctrl); + if (i == 10) + pr_err("retry time over\n"); + + return rc; +} + +int smblib_set_prop_chg_voltage(struct smb_charger *chg, + const union power_supply_propval *val) +{ + chg->fake_chgvol = val->intval; + chg->use_fake_chgvol = true; + power_supply_changed(chg->batt_psy); + + return 0; +} + +int smblib_set_prop_batt_temp(struct smb_charger *chg, + const union power_supply_propval *val) +{ + chg->fake_temp = val->intval; + chg->use_fake_temp = true; + power_supply_changed(chg->batt_psy); + + return 0; +} + +int smblib_set_prop_chg_protect_status(struct smb_charger *chg, + const union power_supply_propval *val) +{ + chg->fake_protect_sts = val->intval; + chg->use_fake_protect_sts = true; + power_supply_changed(chg->batt_psy); + + return 0; +} +int smblib_set_prop_charge_parameter_set(struct smb_charger *chg) +{ + chg->is_power_changed = true; + op_check_battery_temp(chg); + return 0; +} + + int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val) { @@ -2182,6 +2359,10 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, return rc; } + if (chg->vbus_present) { + val->intval = true; + return rc; + } rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", @@ -2219,14 +2400,17 @@ int smblib_get_prop_usb_voltage_max(struct smb_charger *chg, int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { + int rc = 0; + if (!chg->iio.usbin_v_chan || PTR_ERR(chg->iio.usbin_v_chan) == -EPROBE_DEFER) chg->iio.usbin_v_chan = iio_channel_get(chg->dev, "usbin_v"); if (IS_ERR(chg->iio.usbin_v_chan)) return PTR_ERR(chg->iio.usbin_v_chan); - - return iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval); + rc = iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval); + val->intval = val->intval/1000; + return rc; } int smblib_get_prop_usb_current_now(struct smb_charger *chg, @@ -2392,7 +2576,10 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, int smblib_get_prop_pd_allowed(struct smb_charger *chg, union power_supply_propval *val) { - val->intval = get_effective_result(chg->pd_allowed_votable); + if (chg->pd_disabled) + val->intval = 0; + else + val->intval = get_effective_result(chg->pd_allowed_votable); return 0; } @@ -2571,6 +2758,7 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, { int rc = 0; + pr_info("set usb current_max=%d\n", val->intval); if (!chg->pd_active) { rc = smblib_handle_usb_current(chg, val->intval); } else if (chg->system_suspend_supported) { @@ -2625,6 +2813,8 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, smblib_err(chg, "power role %d not supported\n", val->intval); return -EINVAL; } + if (!chg->otg_switch) + power_role = UFP_EN_CMD_BIT; if (chg->wa_flags & TYPEC_PBS_WA_BIT) { if (power_role == UFP_EN_CMD_BIT) { @@ -2698,6 +2888,12 @@ static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active) bool orientation, sink_attached, hvdcp; u8 stat; + if (chg->pd_disabled) + return rc; + + pr_info("set pd_active=%d\n", pd_active); + if (!get_effective_result(chg->pd_allowed_votable)) + return -EINVAL; chg->pd_active = pd_active; if (chg->pd_active) { vote(chg->apsd_disable_votable, PD_VOTER, true, 0); @@ -2946,6 +3142,8 @@ int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, { int rc = 0; + if (chg->pd_disabled) + return rc; if (chg->pd_hard_reset == val->intval) return rc; @@ -3191,6 +3389,20 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) } stat = stat & BATTERY_CHARGER_STATUS_MASK; + + if (stat == TERMINATE_CHARGE) { + /* charge done, disable charge in software also */ + chg->chg_done = true; + pr_err("TERMINATE_CHARGE: chg_done: CAP=%d (Q:%d), VBAT=%d (Q:%d), IBAT=%d (Q:%d), BAT_TEMP=%d\n", + get_prop_batt_capacity(chg), + get_prop_fg_capacity(chg), + get_prop_batt_voltage_now(chg) / 1000, + get_prop_fg_voltage_now(chg) / 1000, + get_prop_batt_current_now(chg) / 1000, + get_prop_fg_current_now(chg) / 1000, + get_prop_batt_temp(chg)); + op_charging_en(chg, false); + } power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } @@ -3238,15 +3450,47 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; struct storm_watch *wdata; - + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_err("return directly because dash is online\n"); + return IRQ_HANDLED; + } smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); if (!chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data) return IRQ_HANDLED; wdata = &chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data->storm_data; reset_storm_count(wdata); + smblib_err(chg, "DEBUG: RESET STORM COUNT FOR POWER_OK\n"); return IRQ_HANDLED; } +char dump_val[2048]; +static inline void op_dump_reg(struct smb_charger *chip, + u16 addr_start, u16 addr_end) +{ + u8 reg; + u16 addr; + char reg_val[19]; + + memset(dump_val, 0, sizeof(dump_val)); + for (addr = addr_start; addr <= addr_end; addr++) { + memset(reg_val, 0, sizeof(reg_val)); + smblib_read(chip, addr, ®); + scnprintf(reg_val, + sizeof(reg_val), "%x=%0x;", addr, reg); + strlcat(dump_val, reg_val, sizeof(dump_val)); + } + pr_info("%s\n", dump_val); +} + +static void op_dump_regs(struct smb_charger *chip) +{ + u16 addr, count; + + count = 0x80; + for (addr = 0x1000; addr <= 0x1700; addr += count) + op_dump_reg(chip, addr, (addr + count)); +} static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { @@ -3269,6 +3513,14 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) struct smb_irq_data *data; struct storm_watch *wdata; + bool last_vbus_present; + + last_vbus_present = chg->vbus_present; + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_err("return directly because dash is online\n"); + return; + } rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); @@ -3309,6 +3561,15 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) struct smb_irq_data *data; struct storm_watch *wdata; + union power_supply_propval vbus_val; + bool last_vbus_present; + + last_vbus_present = chg->vbus_present; + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_err("return directly because dash is online\n"); + return; + } rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); @@ -3319,10 +3580,26 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_set_opt_freq_buck(chg, vbus_rising ? chg->chg_freq.freq_5V : chg->chg_freq.freq_removal); + chg->vbus_present = vbus_rising; + if (last_vbus_present != chg->vbus_present) { + if (chg->vbus_present) { + pr_info("acquire chg_wake_lock\n"); + wake_lock(&chg->chg_wake_lock); + } else { + pr_info("release chg_wake_lock\n"); + wake_unlock(&chg->chg_wake_lock); + } + } if (vbus_rising) { rc = smblib_request_dpdm(chg, true); if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + if (chg->charger_collpse) { + op_set_collapse_fet(chg, 0); + chg->charger_collpse = false; + } + schedule_delayed_work(&chg->op_check_apsd_work, + msecs_to_jiffies(TIME_1000MS)); if (chg->fcc_stepper_mode) vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0); /* Schedule work to enable parallel charger */ @@ -3351,6 +3628,9 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) rc = smblib_request_dpdm(chg, false); if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); + + if (last_vbus_present != chg->vbus_present) + op_handle_usb_removal(chg); } if (chg->micro_usb_mode) @@ -3359,6 +3639,21 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); + if (!vbus_rising) { + rc = smblib_get_prop_usb_voltage_now(chg, &vbus_val); + if (rc < 0) { + pr_err("V fail rc=%d\n", rc); + } else { + if (vbus_val.intval > 3000) { + pr_err("unplg,Vbus=%d", + vbus_val.intval); + op_dump_regs(chg); + } + } + } + + pr_err("IRQ: %s %s\n", + __func__, vbus_rising ? "attached" : "detached"); } irqreturn_t smblib_handle_usb_plugin(int irq, void *data) @@ -3374,6 +3669,13 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) mutex_unlock(&chg->lock); return IRQ_HANDLED; } +void op_handle_usb_plugin(struct smb_charger *chg) +{ + mutex_lock(&chg->lock); + smblib_usb_plugin_locked(chg); + smblib_usb_typec_change(chg); + mutex_unlock(&chg->lock); +} #define USB_WEAK_INPUT_UA 1400000 #define ICL_CHANGE_DELAY_MS 1000 @@ -3625,7 +3927,7 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) */ if (!is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) - vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000); + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 500000); vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0); break; case POWER_SUPPLY_TYPE_USB_CDP: @@ -3641,7 +3943,7 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) * limit ICL to 100mA, the USB driver will enumerate to check * if this is a SDP and appropriately set the current */ - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: @@ -3681,8 +3983,37 @@ static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) } #define HVDCP_DET_MS 2500 +#define DEFAULT_SDP_MA 500 +#define DEFAULT_CDP_MA 1500 +#define DEFAULT_DCP_MA 1500 +#define DEFAULT_AGAING_CHG_MA 1000 +int op_rerun_apsd(struct smb_charger *chg) +{ + union power_supply_propval val; + int rc; + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return rc; + } + + if (!val.intval) + return 0; + /* rerun APSD */ + pr_info("OP Reruning APSD type\n"); + rc = smblib_masked_write(chg, CMD_APSD_REG, + APSD_RERUN_BIT, + APSD_RERUN_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't rerun APSD rc = %d\n", rc); + return rc; + } + return 0; +} static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { + int temp_region, current_limit_ua; const struct apsd_result *apsd_result; if (!rising) @@ -3716,6 +4047,52 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } + if ((apsd_result->bit) == SDP_CHARGER_BIT) + current_limit_ua = DEFAULT_SDP_MA*1000; + else if ((apsd_result->bit) == CDP_CHARGER_BIT) + current_limit_ua = DEFAULT_CDP_MA*1000; + else if ((apsd_result->bit) == DCP_CHARGER_BIT) + current_limit_ua = DEFAULT_DCP_MA*1000; + else if ((apsd_result->bit) == FLOAT_CHARGER_BIT) + current_limit_ua = DEFAULT_DCP_MA*1000; + else if ((apsd_result->bit) == OCP_CHARGER_BIT) + current_limit_ua = DEFAULT_DCP_MA*1000; + + if (chg->is_aging_test) + current_limit_ua = DEFAULT_AGAING_CHG_MA*1000; + vote(chg->usb_icl_votable, + DCP_VOTER, true, current_limit_ua); + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_COLD + && temp_region != BATT_TEMP_HOT) { + op_charging_en(chg, true); + } + + pr_info("apsd result=0x%x, name=%s, psy_type=%d\n", + apsd_result->bit, apsd_result->name, apsd_result->pst); + pr_info("apsd done,current_now=%d\n", + (get_prop_batt_current_now(chg) / 1000)); + if (apsd_result->bit == DCP_CHARGER_BIT + || apsd_result->bit == OCP_CHARGER_BIT + || apsd_result->bit == FLOAT_CHARGER_BIT) { + schedule_delayed_work(&chg->check_switch_dash_work, + msecs_to_jiffies(500)); + } else { + if (!chg->usb_type_redet_done) { + schedule_delayed_work(&chg->re_det_work, + msecs_to_jiffies(TIME_1000MS)); + } else { + schedule_delayed_work( + &chg->non_standard_charger_check_work, + msecs_to_jiffies(TIME_1000MS)); + } + } + chg->op_apsd_done = true; + + /* set allow read extern fg IIC */ + set_property_on_fg(chg, + POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC, true); smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } @@ -3726,6 +4103,11 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc = 0; u8 stat; + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_err("return directly because dash is online\n"); + return IRQ_HANDLED; + } rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { @@ -3734,6 +4116,8 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); + pr_info("APSD_STATUS=0x%02x\n", stat); + if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT) && !chg->uusb_apsd_rerun_done) { /* @@ -4060,8 +4444,10 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) rc); /* enable DRP */ - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - TYPEC_POWER_ROLE_CMD_MASK, 0); + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, + chg->otg_switch ? 0 : UFP_EN_CMD_BIT); if (rc < 0) smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc); @@ -4225,6 +4611,10 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + return IRQ_HANDLED; + } if (chg->micro_usb_mode) { cancel_delayed_work_sync(&chg->uusb_otg_work); @@ -4293,9 +4683,16 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - struct storm_watch *wdata = &irq_data->storm_data; int rc, usb_icl; u8 stat; + union power_supply_propval vbus_val; + + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + pr_err("return directly because dash is online\n"); + return IRQ_HANDLED; + } if (!(chg->wa_flags & BOOST_BACK_WA)) return IRQ_HANDLED; @@ -4315,32 +4712,17 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) return IRQ_HANDLED; if (is_storming(&irq_data->storm_data)) { - /* This could be a weak charger reduce ICL */ - if (!is_client_vote_enabled(chg->usb_icl_votable, - WEAK_CHARGER_VOTER)) { - smblib_err(chg, - "Weak charger detected: voting %dmA ICL\n", - *chg->weak_chg_icl_ua / 1000); - vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, - true, *chg->weak_chg_icl_ua); - /* - * reset storm data and set the storm threshold - * to 3 for reverse boost detection. - */ - update_storm_count(wdata, BOOST_BACK_STORM_COUNT); - } else { - smblib_err(chg, - "Reverse boost detected: voting 0mA to suspend input\n"); - vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); - vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0); - /* - * Remove the boost-back vote after a delay, to avoid - * permanently suspending the input if the boost-back - * condition is unintentionally hit. - */ - schedule_delayed_work(&chg->bb_removal_work, - msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS)); - } + /*Use the setting of 0x1380 and 0x1365 is useful*/ + smblib_err(chg, "Reverse boost detected\n"); + rc = smblib_get_prop_usb_voltage_now(chg, &vbus_val); + if (rc < 0) + pr_err("fail to read usb_voltage rc=%d\n", rc); + else if (vbus_val.intval >= 2500) + pr_err("vbus_val.intval=%d\n", vbus_val.intval); + chg->revert_boost_trigger = true; + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); + schedule_delayed_work(&chg->recovery_suspend_work, + msecs_to_jiffies(TIME_100MS)); } return IRQ_HANDLED; @@ -4364,6 +4746,2088 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data) return IRQ_HANDLED; } +irqreturn_t smblib_handle_aicl_done(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + int icl_ma, rc; + + rc = smblib_get_charge_param(chg, + &chg->param.icl_stat, &icl_ma); + if (rc < 0) { + pr_err("Couldn't get ICL status rc=%d\n", rc); + return IRQ_HANDLED; + } + + pr_info("IRQ: %s AICL result=%d\n", irq_data->name, icl_ma); + return IRQ_HANDLED; +} +int op_get_aicl_result(struct smb_charger *chg) +{ + int icl_ma, rc; + + rc = smblib_get_charge_param(chg, + &chg->param.icl_stat, &icl_ma); + if (rc < 0) { + pr_err("Couldn't get ICL status rc=%d\n", rc); + return -EINVAL; + } + + pr_info("AICL result=%d\n", icl_ma); + return icl_ma; +} + +static int get_property_from_fg(struct smb_charger *chg, + enum power_supply_property prop, int *val) +{ + int rc; + union power_supply_propval ret = {0, }; + + if (!chg->bms_psy) + chg->bms_psy = power_supply_get_by_name("bms"); + + if (chg->bms_psy) { + rc = power_supply_get_property(chg->bms_psy, prop, &ret); + if (rc) { + pr_err("bms psy doesn't support reading prop %d rc = %d\n", + prop, rc); + return rc; + } + *val = ret.intval; + } else { + pr_err("no bms psy found\n"); + return -EINVAL; + } + + return rc; +} + +static int set_property_on_fg(struct smb_charger *chg, + enum power_supply_property prop, int val) +{ + int rc; + union power_supply_propval ret = {0, }; + + if (!chg->bms_psy) + chg->bms_psy = power_supply_get_by_name("bms"); + + if (chg->bms_psy) { + ret.intval = val; + rc = power_supply_set_property(chg->bms_psy, prop, &ret); + if (rc) + pr_err("bms psy does not allow updating prop %d rc = %d\n", + prop, rc); + } else { + pr_err("no bms psy found\n"); + return -EINVAL; + } + + return rc; +} + +static int op_charging_en(struct smb_charger *chg, bool en) +{ + int rc; + + pr_err("enable=%d\n", en); + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + en ? CHARGING_ENABLE_CMD_BIT : 0); + if (rc < 0) { + pr_err("Couldn't %s charging rc=%d\n", + en ? "enable" : "disable", rc); + return rc; + } + + return 0; +} + +static bool is_usb_present(struct smb_charger *chg) +{ + int rc = 0; + u8 stat; + + rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + pr_err("Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + return rc; + } + pr_debug("TYPE_C_STATUS_4 = 0x%02x\n", stat); + return (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); +} + +static bool op_get_fast_low_temp_full(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->get_fast_low_temp_full) + return fast_charger->get_fast_low_temp_full(); + pr_err("no fast_charger register found\n"); + return false; +} + +static bool get_fastchg_firmware_updated_status(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->get_fastchg_firmware_already_updated) + return fast_charger->get_fastchg_firmware_already_updated(); + pr_err("no fast_charger register found\n"); + return false; +} + +static bool get_prop_fast_switch_to_normal(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->fast_switch_to_normal) + return fast_charger->fast_switch_to_normal(); + pr_err("no fast_charger register found\n"); + return false; +} + +bool is_fastchg_allowed(struct smb_charger *chg) +{ + int temp; + static int pre_temp; + static bool pre_switch_to_normal; + bool low_temp_full, switch_to_normal, fw_updated; + + temp = get_prop_batt_temp(chg); + low_temp_full = op_get_fast_low_temp_full(chg); + fw_updated = get_fastchg_firmware_updated_status(chg); + + if (!fw_updated) + return false; + if (chg->usb_enum_status) + return false; + if (temp < 165 || temp > 430) { + if (temp != pre_temp) + pr_err("temp=%d is not allow to swith fastchg\n", temp); + pre_temp = temp; + return false; + } + + switch_to_normal = get_prop_fast_switch_to_normal(chg); + if (pre_switch_to_normal != switch_to_normal) + pr_info("switch_to_normal =%d\n", switch_to_normal); + if (switch_to_normal) + return false; + + return true; +} + +bool get_oem_charge_done_status(void) +{ +#ifdef CONFIG_OP_DEBUG_CHG + return false; +#else + if (g_chg->is_aging_test) + return false; +#endif + if (g_chg) + return (g_chg->chg_done || g_chg->recharge_status); + else + return false; +} + +static void op_handle_usb_removal(struct smb_charger *chg) +{ + op_set_fast_chg_allow(chg, false); + set_prop_fast_switch_to_normal_false(chg); + set_usb_switch(chg, false); + set_dash_charger_present(false); + + chg->chg_ovp = false; + chg->dash_on = false; + chg->chg_done = false; + chg->time_out = false; + chg->recharge_status = false; + chg->usb_enum_status = false; + chg->non_std_chg_present = false; + chg->usb_type_redet_done = false; + chg->boot_usb_present = false; + chg->revert_boost_trigger = false; + chg->non_stand_chg_current = 0; + chg->non_stand_chg_count = 0; + chg->redet_count = 0; + chg->dump_count = 0; + chg->op_apsd_done = 0; + chg->ck_dash_count = 0; + chg->re_trigr_dash_done = 0; + chg->recovery_boost_count = 0; + op_battery_temp_region_set(chg, BATT_TEMP_INVALID); +} + +int update_dash_unplug_status(void) +{ + int rc; + union power_supply_propval vbus_val; + + rc = smblib_get_prop_usb_voltage_now(g_chg, &vbus_val); + if (rc < 0) + pr_err("failed to read usb_voltage rc=%d\n", rc); + else if (vbus_val.intval <= 2500) { + op_handle_usb_plugin(g_chg); + smblib_update_usb_type(g_chg); + power_supply_changed(g_chg->usb_psy); + } + + return 0; +} + +static int op_set_collapse_fet(struct smb_charger *chg, bool on) +{ + int rc = 0; + u8 stat; + + rc = smblib_masked_write(chg, USBIN_5V_AICL_THRESHOLD_CFG_REG, + BIT(0) | BIT(1), on ? 0 : BIT(0) | BIT(1)); + if (rc < 0) { + smblib_err(chg, "Couldn't write %s to 0x%x rc=%d\n", + on ? "on" : "off", USBIN_5V_AICL_THRESHOLD_CFG_REG, rc); + return rc; + } + + rc = smblib_read(chg, USBIN_5V_AICL_THRESHOLD_CFG_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read 0x%x rc=%d\n", + USBIN_5V_AICL_THRESHOLD_CFG_REG, rc); + return rc; + } + pr_info("USBIN_5V_AICL_THRESHOLD_CFG_REG(0x%x)=0x%x\n", + USBIN_5V_AICL_THRESHOLD_CFG_REG, stat); + + rc = smblib_masked_write(chg, USBIN_CONT_AICL_THRESHOLD_CFG_REG, + BIT(0) | BIT(1), on ? 0 : BIT(0) | BIT(1)); + if (rc < 0) { + smblib_err(chg, "Couldn't write %s to 0x%x rc=%d\n", + on ? "on" : "off", USBIN_CONT_AICL_THRESHOLD_CFG_REG, + rc); + return rc; + } + + rc = smblib_read(chg, USBIN_CONT_AICL_THRESHOLD_CFG_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read 0x%x rc=%d\n", + USBIN_CONT_AICL_THRESHOLD_CFG_REG, rc); + return rc; + } + pr_info("USBIN_CONT_AICL_THRESHOLD_CFG_REG(0x%x)=0x%x\n", + USBIN_CONT_AICL_THRESHOLD_CFG_REG, stat); + + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + SUSPEND_ON_COLLAPSE_USBIN_BIT + | USBIN_HV_COLLAPSE_RESPONSE_BIT + | USBIN_LV_COLLAPSE_RESPONSE_BIT + | USBIN_AICL_RERUN_EN_BIT, + on ? 0 : SUSPEND_ON_COLLAPSE_USBIN_BIT + | USBIN_HV_COLLAPSE_RESPONSE_BIT + | USBIN_LV_COLLAPSE_RESPONSE_BIT); + if (rc < 0) { + smblib_err(chg, + "Couldn't write %s to 0x%x rc=%d\n", + on ? "on" : "off", USBIN_AICL_OPTIONS_CFG_REG, rc); + return rc; + } + + rc = smblib_read(chg, USBIN_AICL_OPTIONS_CFG_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read 0x%x rc=%d\n", + USBIN_AICL_OPTIONS_CFG_REG, rc); + return rc; + } + pr_info("USBIN_AICL_OPTIONS_CFG_REG(0x%x)=0x%x\n", + USBIN_AICL_OPTIONS_CFG_REG, stat); + + rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, BIT(0) + | BIT(1), on ? 0 : BIT(0) | BIT(1)); + if (rc < 0) { + smblib_err(chg, "Couldn't write %s to 0x%x rc=%d\n", + on ? "on" : "off", USBIN_LOAD_CFG_REG, rc); + return rc; + } + + rc = smblib_read(chg, USBIN_LOAD_CFG_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read 0x%x rc=%d\n", + USBIN_LOAD_CFG_REG, rc); + return rc; + } + pr_info("USBIN_LOAD_CFG_REG(0x%x)=0x%x\n", + USBIN_LOAD_CFG_REG, stat); + + return rc; +} + + +int op_handle_switcher_power_ok(void) +{ + int rc; + u8 stat; + union power_supply_propval vbus_val; + + if (!g_chg) + return 0; + if (!(g_chg->wa_flags & BOOST_BACK_WA)) + return 0; + rc = smblib_read(g_chg, POWER_PATH_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(g_chg, + "Couldn't read POWER_PATH_STATUS rc=%d\n", rc); + return 0; + } + smblib_err(g_chg, "POWER_PATH_STATUS stat=0x%x\n", stat); + + if ((stat & USE_USBIN_BIT) && + get_effective_result(g_chg->usb_icl_votable) + < USBIN_25MA) + return 0; + + if (stat & USE_DCIN_BIT) + return 0; + usleep_range(50000, 50002); + rc = smblib_get_prop_usb_voltage_now(g_chg, &vbus_val); + if (rc < 0) { + pr_err("fail to read usb_voltage rc=%d\n", rc); + } else if (vbus_val.intval >= 2500) { + pr_err("vbus_val.intval=%d\n", vbus_val.intval); + vote(g_chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); + schedule_delayed_work(&g_chg->recovery_suspend_work, + msecs_to_jiffies(TIME_100MS)); + smblib_err(g_chg, "OP Reverse boost detected\n"); + } + + return 0; +} + +int op_contrl(int enable, bool check_power_ok) +{ + pr_info("%s, en=%d\n", __func__, enable); + if (!g_chg) + return 0; + if (enable) { + if (check_power_ok) + op_handle_switcher_power_ok(); + } else{ + op_set_collapse_fet(g_chg, enable); + } + return 0; +} + +bool get_prop_fast_chg_started(struct smb_charger *chg) +{ + if (get_prop_fast_adapter_update(chg) + == ADAPTER_FW_NEED_UPDATE) + return true; + if (fast_charger && fast_charger->fast_chg_started) + return fast_charger->fast_chg_started(); + pr_err("no fast_charger register found\n"); + return false; +} + +int get_prop_fast_adapter_update(struct smb_charger *chg) +{ + int update_status; + + if (fast_charger && fast_charger->get_adapter_update) + update_status = fast_charger->get_adapter_update(); + else { + pr_err("no fast_charger register found\n"); + update_status = ADAPTER_FW_UPDATE_NONE; + } + return update_status; +} + +static bool set_prop_fast_switch_to_normal_false(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->set_switch_to_noraml_false) + return fast_charger->set_switch_to_noraml_false(); + pr_err("no fast_charger register found\n"); + return false; +} + +bool op_get_fastchg_ing(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->get_fast_chg_ing) + return fast_charger->get_fast_chg_ing(); + pr_err("no fast_charger register found\n"); + return false; +} + +bool op_set_fast_chg_allow(struct smb_charger *chg, bool enable) +{ + if (fast_charger && fast_charger->set_fast_chg_allow) + return fast_charger->set_fast_chg_allow(enable); + pr_err("no fast_charger register found\n"); + return false; +} + +static bool op_get_fast_chg_allow(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->get_fast_chg_allow) + return fast_charger->get_fast_chg_allow(); + pr_err("no fast_charger register found\n"); + return false; +} + +static bool op_is_usb_switch_on(struct smb_charger *chg) +{ + if (fast_charger && fast_charger->is_usb_switch_on) + return fast_charger->is_usb_switch_on(); + + pr_err("no fast_charger register found\n"); + return false; +} + +static enum batt_status_type op_battery_status_get(struct smb_charger *chg) +{ + return chg->battery_status; +} + +static enum temp_region_type op_battery_temp_region_get(struct smb_charger *chg) +{ + return chg->mBattTempRegion; +} + +int fuelgauge_battery_temp_region_get(void) +{ + if (!g_chg) + return BATT_TEMP_NORMAL; + + return op_battery_temp_region_get(g_chg); +} + +static void op_battery_status_set(struct smb_charger *chg, + enum batt_status_type battery_status) +{ + chg->battery_status = battery_status; +} + +static void op_battery_temp_region_set(struct smb_charger *chg, + enum temp_region_type batt_temp_region) +{ + chg->mBattTempRegion = batt_temp_region; + pr_err("set temp_region=%d\n", chg->mBattTempRegion); +} + +static void set_prop_batt_health(struct smb_charger *chg, int batt_health) +{ + chg->batt_health = batt_health; +} + +static void set_usb_switch(struct smb_charger *chg, bool enable) +{ + int retrger_time; + + if (!fast_charger) { + pr_err("no fast_charger register found\n"); + return; + } + + if (enable) { + pr_err("switch on fastchg\n"); + if (chg->boot_usb_present && chg->re_trigr_dash_done) { + vote(chg->usb_icl_votable, AICL_RERUN_VOTER, + true, 0); + usleep_range(500000, 510000); + vote(chg->usb_icl_votable, AICL_RERUN_VOTER, + true, DEFAULT_DCP_MA*1000); + } + set_mcu_en_gpio_value(1); + usleep_range(10000, 10002); + usb_sw_gpio_set(1); + usleep_range(10000, 10002); + mcu_en_gpio_set(0); + if (chg->boot_usb_present) + retrger_time = TIME_3S; + else + retrger_time = TIME_200MS; + if (!chg->re_trigr_dash_done) + schedule_delayed_work(&chg->rechk_sw_dsh_work, + msecs_to_jiffies(retrger_time)); + } else { + pr_err("switch off fastchg\n"); + usb_sw_gpio_set(0); + mcu_en_gpio_set(1); + } +} + +static void switch_fast_chg(struct smb_charger *chg) +{ + bool fastchg_allowed, is_allowed; + static bool pre_fastchg_allowed, pre_is_allowed; + + mutex_lock(&chg->sw_dash_lock); + if (op_is_usb_switch_on(chg)) { + mutex_unlock(&chg->sw_dash_lock); + return; + } + if (!is_usb_present(chg)) { + mutex_unlock(&chg->sw_dash_lock); + return; + } + + fastchg_allowed = op_get_fast_chg_allow(chg); + if (pre_fastchg_allowed != fastchg_allowed) { + pre_fastchg_allowed = fastchg_allowed; + pr_info("fastchg_allowed = %d\n", fastchg_allowed); + } + if (!fastchg_allowed) { + is_allowed = is_fastchg_allowed(chg); + if (pre_is_allowed != is_allowed) { + pre_is_allowed = is_allowed; + pr_info("is_allowed = %d\n", is_allowed); + } + if (is_allowed) { + set_usb_switch(chg, true); + op_set_fast_chg_allow(chg, true); + } + } + mutex_unlock(&chg->sw_dash_lock); +} + +static void op_re_kick_allowed_voltage(struct smb_charger *chg) +{ + const struct apsd_result *apsd_result; + + if (!is_usb_present(chg)) + return; + + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->bit == SDP_CHARGER_BIT) + return; + + pr_info("re-kick allowed voltage\n"); + smblib_set_usb_pd_allowed_voltage(chg, MICRO_9V, MICRO_9V); + msleep(500); + smblib_set_usb_pd_allowed_voltage(chg, MICRO_5V, MICRO_5V); +} + +static void op_re_kick_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + re_kick_work.work); + + if (chg->vbus_present) { + op_re_kick_allowed_voltage(chg); + schedule_delayed_work(&chg->check_switch_dash_work, + msecs_to_jiffies(500)); + } +} +static void retrigger_dash_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + rechk_sw_dsh_work.work); + pr_debug("chg->ck_dash_count=%d\n", chg->ck_dash_count); + if (chg->usb_enum_status) + return; + if (chg->dash_present) { + chg->ck_dash_count = 0; + return; + } + if (!chg->vbus_present) { + chg->ck_dash_count = 0; + return; + } + if (chg->ck_dash_count >= DASH_CHECK_COUNT) { + pr_info("retrger dash\n"); + chg->re_trigr_dash_done = true; + set_usb_switch(chg, false); + set_usb_switch(chg, true); + chg->ck_dash_count = 0; + } else { + chg->ck_dash_count++; + schedule_delayed_work(&chg->rechk_sw_dsh_work, + msecs_to_jiffies(TIME_200MS)); + } +} + +static void op_chek_apsd_done_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + op_check_apsd_work.work); + union power_supply_propval vbus_val; + int rc; + const struct apsd_result *apsd_result; + + pr_debug("chg->ck_apsd_count=%d\n", chg->ck_apsd_count); + if (chg->usb_enum_status || chg->op_apsd_done) { + chg->ck_apsd_count = 0; + return; + } + rc = smblib_get_prop_usb_voltage_now(chg, &vbus_val); + if (rc < 0) { + chg->ck_apsd_count = 0; + pr_info("failed to read usb_voltage rc=%d\n", rc); + return; + } + if (vbus_val.intval < 2500) { + pr_info("vbus less 2.5v\n"); + chg->ck_apsd_count = 0; + return; + } + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->bit) { + chg->ck_apsd_count = 0; + return; + } + + if (chg->ck_apsd_count >= APSD_CHECK_COUTNT) { + pr_info("apsd done error\n"); + chg->ck_apsd_count = 0; + op_dump_regs(chg); + op_rerun_apsd(chg); + } else { + chg->ck_apsd_count++; + schedule_delayed_work(&chg->op_check_apsd_work, + msecs_to_jiffies(TIME_1000MS)); + } +} + + + +static void op_recovery_usb_suspend_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + recovery_suspend_work.work); + int effect_result; + + if (chg->recovery_boost_count >= BOOST_BACK_COUNT) { + pr_info("recovery revert boost\n"); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + if (is_usb_present(chg)) { + effect_result = + get_effective_result(chg->usb_icl_votable); + pr_info("effect_result=%d\n", effect_result); + if (effect_result > DEFAULT_AGAING_CHG_MA*1000) + vote(chg->usb_icl_votable, DCP_VOTER, + true, (effect_result - USBIN_150MA)); + } + msleep(1000); + chg->revert_boost_trigger = false; + } else { + chg->recovery_boost_count++; + schedule_delayed_work(&chg->recovery_suspend_work, + msecs_to_jiffies(TIME_100MS)); + } +} +static void op_check_allow_switch_dash_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, check_switch_dash_work); + const struct apsd_result *apsd_result; + + if (!is_usb_present(chg)) + return; + if (chg->usb_enum_status) + return; + + apsd_result = smblib_get_apsd_result(chg); + if (((apsd_result->bit != SDP_CHARGER_BIT + && apsd_result->bit != CDP_CHARGER_BIT) + && apsd_result->bit) + || chg->non_std_chg_present) + switch_fast_chg(chg); +} + +int check_allow_switch_dash(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + schedule_delayed_work(&chg->check_switch_dash_work, + msecs_to_jiffies(500)); + return 0; +} + +#define DEFAULT_WALL_CHG_MA 1800 +static int set_dash_charger_present(int status) +{ + int charger_present; + bool pre_dash_present; + + if (g_chg) { + pre_dash_present = g_chg->dash_present; + charger_present = is_usb_present(g_chg); + g_chg->dash_present = status && charger_present; + if (g_chg->dash_present && !pre_dash_present) { + pr_err("set dash online\n"); + g_chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + vote(g_chg->usb_icl_votable, PD_VOTER, true, + DEFAULT_WALL_CHG_MA * 1000); + } + power_supply_changed(g_chg->batt_psy); + pr_info("dash_present = %d, charger_present = %d\n", + g_chg->dash_present, charger_present); + } else { + pr_err("set_dash_charger_present error\n"); + } + + return 0; +} +#ifndef CONFIG_OP_DEBUG_CHG +static void op_check_charge_timeout(struct smb_charger *chg) +{ + static int batt_status, count; + + if (chg->chg_done || chg->is_aging_test) + return; + + batt_status = get_prop_batt_status(chg); + if (chg->vbus_present + && batt_status == POWER_SUPPLY_STATUS_CHARGING) + count++; + else + count = 0; + + if (count > CHG_TIMEOUT_COUNT) { + pr_err("chg timeout! stop chaging now\n"); + op_charging_en(chg, false); + chg->time_out = true; + } +} +#endif +static int get_prop_batt_present(struct smb_charger *chg) +{ + int rc; + u8 stat; + + rc = smblib_read(chg, BATIF_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + pr_err("Couldn't read BATIF_INT_RT_STS rc=%d\n", rc); + return rc; + } + + return !(stat & (BAT_THERM_OR_ID_MISSING_RT_STS_BIT + | BAT_TERMINAL_MISSING_RT_STS_BIT)); +} + +#define DEFAULT_BATT_CAPACITY 50 +static int get_prop_batt_capacity(struct smb_charger *chg) +{ + int capacity, rc; + + if (chg->fake_capacity >= 0) + return chg->fake_capacity; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_CAPACITY, &capacity); + if (rc) { + pr_err("Couldn't get capacity rc=%d\n", rc); + capacity = DEFAULT_BATT_CAPACITY; + } + + return capacity; +} + +#define DEFAULT_BATT_TEMP 200 +static int get_prop_batt_temp(struct smb_charger *chg) +{ + int temp, rc; + + if (chg->use_fake_temp) + return chg->fake_temp; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_TEMP, &temp); + if (rc) { + pr_err("Couldn't get temperature rc=%d\n", rc); + temp = DEFAULT_BATT_TEMP; + } + + return temp; +} + +#define DEFAULT_BATT_CURRENT_NOW 0 +static int get_prop_batt_current_now(struct smb_charger *chg) +{ + int ua, rc; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_CURRENT_NOW, &ua); + if (rc) { + pr_err("Couldn't get current rc=%d\n", rc); + ua = DEFAULT_BATT_CURRENT_NOW; + } + + return ua; +} + +#define DEFAULT_BATT_VOLTAGE_NOW 0 +static int get_prop_batt_voltage_now(struct smb_charger *chg) +{ + int uv, rc; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv); + if (rc) { + pr_err("Couldn't get voltage rc=%d\n", rc); + uv = DEFAULT_BATT_VOLTAGE_NOW; + } + + return uv; +} + +static int get_prop_fg_capacity(struct smb_charger *chg) +{ + int capacity, rc; + + if (chg->fake_capacity >= 0) + return chg->fake_capacity; + + rc = get_property_from_fg(chg, + POWER_SUPPLY_PROP_FG_CAPACITY, &capacity); + if (rc) { + pr_err("Couldn't get capacity rc=%d\n", rc); + capacity = DEFAULT_BATT_CAPACITY; + } + + return capacity; +} + +static int get_prop_fg_current_now(struct smb_charger *chg) +{ + int ua, rc; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_FG_CURRENT_NOW, &ua); + if (rc) { + pr_err("Couldn't get current rc=%d\n", rc); + ua = DEFAULT_BATT_CURRENT_NOW; + } + + return ua; +} + +static int get_prop_fg_voltage_now(struct smb_charger *chg) +{ + int uv, rc; + + rc = get_property_from_fg(chg, POWER_SUPPLY_PROP_FG_VOLTAGE_NOW, &uv); + if (rc) { + pr_err("Couldn't get voltage rc=%d\n", rc); + uv = DEFAULT_BATT_VOLTAGE_NOW; + } + + return uv; +} + +int get_prop_batt_status(struct smb_charger *chg) +{ + int capacity, batt_status, rc; + enum temp_region_type temp_region; + union power_supply_propval pval = {0, }; + + temp_region = op_battery_temp_region_get(chg); + capacity = get_prop_batt_capacity(chg); + chg->dash_on = get_prop_fast_chg_started(chg); + if ((chg->chg_done || chg->recharge_status) + && (temp_region == BATT_TEMP_COOL + || temp_region == BATT_TEMP_LITTLE_COOL + || temp_region == BATT_TEMP_PRE_NORMAL + || temp_region == BATT_TEMP_NORMAL) + && capacity > 90) { + return POWER_SUPPLY_STATUS_FULL; + } else if (chg->dash_on) { + return POWER_SUPPLY_STATUS_CHARGING; + } + if (chg->revert_boost_trigger && chg->vbus_present) + return POWER_SUPPLY_STATUS_CHARGING; + + rc = smblib_get_prop_batt_status(chg, &pval); + if (rc) + batt_status = 0; + else + batt_status = pval.intval; + + return batt_status; +} + +int get_charging_status(void) +{ + int rc; + union power_supply_propval pval = {0, }; + + if (!g_chg) + return POWER_SUPPLY_STATUS_DISCHARGING; + + rc = smblib_get_prop_batt_status(g_chg, &pval); + if (rc) + return POWER_SUPPLY_STATUS_UNKNOWN; + + return pval.intval; +} + +void set_chg_ibat_vbat_max( + struct smb_charger *chg, int ibat, int vfloat) +{ + pr_err("set ibatmax=%d and set vbatmax=%d\n", + ibat, vfloat); + + vote(chg->fcc_votable, + DEFAULT_VOTER, true, ibat * 1000); + vote(chg->fv_votable, + DEFAULT_VOTER, true, vfloat * 1000); + + /* set cc to cv 100mv lower than vfloat */ + set_property_on_fg(chg, POWER_SUPPLY_PROP_CC_TO_CV_POINT, vfloat - 100); +} + +/* Tbatt < -3C */ +static int handle_batt_temp_cold(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_COLD || chg->is_power_changed) { + pr_err("triggered\n"); + chg->is_power_changed = false; + + op_charging_en(chg, false); + op_battery_temp_region_set(chg, BATT_TEMP_COLD); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0 + BATT_TEMP_HYST; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_COLD); + } + + return 0; +} + +/* -3C <= Tbatt <= 0C */ +static int handle_batt_temp_little_cold(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_LITTLE_COLD + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->recharge_pending = false; + chg->is_power_changed = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_LITTLE_COLD], + chg->vbatmax[BATT_TEMP_LITTLE_COLD]); + op_battery_temp_region_set(chg, BATT_TEMP_LITTLE_COLD); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1 + BATT_TEMP_HYST; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} + +/* 0C < Tbatt <= 5C*/ +static int handle_batt_temp_cool(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_COOL + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->recharge_pending = false; + chg->is_power_changed = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_COOL], + chg->vbatmax[BATT_TEMP_COOL]); + op_battery_temp_region_set(chg, BATT_TEMP_COOL); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2 + BATT_TEMP_HYST; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} +/* 5C < Tbatt <= 12C */ +static int handle_batt_temp_little_cool(struct smb_charger *chg) +{ + int temp_region, vbat_mv; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_LITTLE_COOL + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->recharge_pending = false; + chg->is_power_changed = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + vbat_mv = get_prop_batt_voltage_now(chg) / 1000; + if (vbat_mv > 4180) { + set_chg_ibat_vbat_max(chg, 450, + chg->vbatmax[BATT_TEMP_LITTLE_COOL]); + chg->temp_littel_cool_set_current_0_point_25c = false; + } else { + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_LITTLE_COOL], + chg->vbatmax[BATT_TEMP_LITTLE_COOL]); + chg->temp_littel_cool_set_current_0_point_25c = true; + } + op_battery_temp_region_set(chg, BATT_TEMP_LITTLE_COOL); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3 + BATT_TEMP_HYST; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} + +/* 12C < Tbatt < 22C */ +static int handle_batt_temp_prenormal(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if (temp_region != BATT_TEMP_PRE_NORMAL + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->recharge_pending = false; + chg->is_power_changed = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_PRE_NORMAL], + chg->vbatmax[BATT_TEMP_PRE_NORMAL]); + op_battery_temp_region_set(chg, BATT_TEMP_PRE_NORMAL); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4 + BATT_TEMP_HYST; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} + +/* 15C < Tbatt < 45C */ +static int handle_batt_temp_normal(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if ((temp_region != BATT_TEMP_NORMAL) + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->recharge_pending = false; + chg->is_power_changed = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_NORMAL], + chg->vbatmax[BATT_TEMP_NORMAL]); + op_battery_temp_region_set(chg, BATT_TEMP_NORMAL); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} + +/* 45C <= Tbatt <= 55C */ +static int handle_batt_temp_warm(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + if (chg->chg_ovp) + return 0; + + temp_region = op_battery_temp_region_get(chg); + if ((temp_region != BATT_TEMP_WARM) + || chg->is_power_changed || chg->recharge_pending) { + pr_err("triggered\n"); + chg->is_power_changed = false; + chg->recharge_pending = false; + + if (temp_region == BATT_TEMP_HOT || + temp_region == BATT_TEMP_COLD) + op_charging_en(chg, true); + + set_chg_ibat_vbat_max(chg, + chg->ibatmax[BATT_TEMP_WARM], + chg->vbatmax[BATT_TEMP_WARM]); + op_battery_temp_region_set(chg, BATT_TEMP_WARM); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5 - BATT_TEMP_HYST; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + } + + return 0; +} + +/* 55C < Tbatt */ +static int handle_batt_temp_hot(struct smb_charger *chg) +{ + enum temp_region_type temp_region; + + temp_region = op_battery_temp_region_get(chg); + if ((temp_region != BATT_TEMP_HOT) + || chg->is_power_changed) { + pr_err("triggered\n"); + chg->is_power_changed = false; + op_charging_en(chg, false); + op_battery_temp_region_set(chg, BATT_TEMP_HOT); + + /* Update the temperature boundaries */ + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = + chg->BATT_TEMP_T6 - BATT_TEMP_HYST; + /* from hot to warm */ + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_OVERHEAT); + } + + return 0; +} + +static int op_check_battery_temp(struct smb_charger *chg) +{ + int temp, rc = -1; + + if (!chg->vbus_present) + return rc; + + temp = get_prop_batt_temp(chg); + if (temp < chg->mBattTempBoundT0) /* COLD */ + rc = handle_batt_temp_cold(chg); + else if (temp >= chg->mBattTempBoundT0 && + temp < chg->mBattTempBoundT1) /* LITTLE_COLD */ + rc = handle_batt_temp_little_cold(chg); + else if (temp >= chg->mBattTempBoundT1 && + temp < chg->mBattTempBoundT2) /* COOL */ + rc = handle_batt_temp_cool(chg); + else if (temp >= chg->mBattTempBoundT2 && + temp < chg->mBattTempBoundT3) /* LITTLE_COOL */ + rc = handle_batt_temp_little_cool(chg); + else if (temp >= chg->mBattTempBoundT3 && + temp < chg->mBattTempBoundT4) /* PRE_NORMAL */ + rc = handle_batt_temp_prenormal(chg); + else if (temp >= chg->mBattTempBoundT4 && + temp < chg->mBattTempBoundT5) /* NORMAL */ + rc = handle_batt_temp_normal(chg); + else if (temp >= chg->mBattTempBoundT5 && + temp <= chg->mBattTempBoundT6) /* WARM */ + rc = handle_batt_temp_warm(chg); + else if (temp > chg->mBattTempBoundT6) /* HOT */ + rc = handle_batt_temp_hot(chg); + + return rc; +} + +void op_charge_info_init(struct smb_charger *chg) +{ + op_battery_temp_region_set(chg, BATT_TEMP_NORMAL); + + chg->mBattTempBoundT0 = chg->BATT_TEMP_T0; + chg->mBattTempBoundT1 = chg->BATT_TEMP_T1; + chg->mBattTempBoundT2 = chg->BATT_TEMP_T2; + chg->mBattTempBoundT3 = chg->BATT_TEMP_T3; + chg->mBattTempBoundT4 = chg->BATT_TEMP_T4; + chg->mBattTempBoundT5 = chg->BATT_TEMP_T5; + chg->mBattTempBoundT6 = chg->BATT_TEMP_T6; + chg->chg_ovp = false; + chg->is_power_changed = false; + chg->chg_done = false; + chg->recharge_pending = false; + chg->recharge_status = false; + chg->temp_littel_cool_set_current_0_point_25c = false; + chg->oem_lcd_is_on = false; + chg->time_out = false; + chg->battery_status = BATT_STATUS_GOOD; + chg->disable_normal_chg_for_dash = false; + chg->usb_enum_status = false; + chg->non_std_chg_present = false; +} + +static int op_handle_battery_uovp(struct smb_charger *chg) +{ + pr_err("vbat is over voltage, stop charging\n"); + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_OVERVOLTAGE); + op_charging_en(chg, false); + + return 0; +} + +static int op_handle_battery_restore_from_uovp(struct smb_charger *chg) +{ + pr_err("vbat is back to normal, start charging\n"); + /* restore charging form battery ovp */ + op_charging_en(chg, true); + set_prop_batt_health(chg, POWER_SUPPLY_HEALTH_GOOD); + + return 0; +} + +static void op_check_battery_uovp(struct smb_charger *chg) +{ + int vbat_mv = 0; + enum batt_status_type battery_status_pre; + + if (!chg->vbus_present) + return; + + battery_status_pre = op_battery_status_get(chg); + vbat_mv = get_prop_batt_voltage_now(chg) / 1000; + pr_debug("bat vol:%d\n", vbat_mv); + if (vbat_mv > BATT_SOFT_OVP_MV) { + if (battery_status_pre == BATT_STATUS_GOOD) { + pr_err("BATTERY_SOFT_OVP_VOLTAGE\n"); + op_battery_status_set(chg, BATT_STATUS_BAD); + op_handle_battery_uovp(chg); + } + } else { + if (battery_status_pre == BATT_STATUS_BAD) { + pr_err("battery_restore_from_uovp\n"); + op_battery_status_set(chg, BATT_STATUS_GOOD); + op_handle_battery_restore_from_uovp(chg); + } + } + +} +int op_get_charg_en(struct smb_charger *chg, int *chg_enabled) +{ + int rc = 0; + u8 temp; + + rc = smblib_read(chg, CHARGING_ENABLE_CMD_REG, &temp); + if (rc < 0) { + smblib_err(chg, "Couldn't read chg en rc=%d\n", rc); + return rc; + } + *chg_enabled = temp & CHARGING_ENABLE_CMD_BIT; + + return rc; +} + +static void op_check_charger_collapse(struct smb_charger *chg) +{ + int rc, is_usb_supend, curr, chg_en; + u8 stat, chger_stat, pwer_source_stats; + + if (!chg->vbus_present) + return; + if (chg->dash_present) + return; + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &chger_stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", + rc); + } + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &pwer_source_stats); + if (rc < 0) { + smblib_err(chg, "Couldn't read AICL_STATUS_REG rc=%d\n", + rc); + } + smblib_get_usb_suspend(chg, &is_usb_supend); + op_get_charg_en(chg, &chg_en); + pr_debug("chger_stat=0x%x, aicl_stats =0x%x, chg_en =%d\n", + chger_stat, pwer_source_stats, chg_en); + curr = get_prop_batt_current_now(chg) / 1000; + stat = !chg->chg_done + && !is_usb_supend + && (curr > 20) + && chg_en + && ((CC_SOFT_TERMINATE_BIT & chger_stat) + || (pwer_source_stats == 0x72)); + + if (stat && !chg->charger_collpse) { + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + SUSPEND_ON_COLLAPSE_USBIN_BIT + |USBIN_AICL_START_AT_MAX_BIT + | USBIN_AICL_ADC_EN_BIT + |USBIN_AICL_RERUN_EN_BIT, USBIN_AICL_RERUN_EN_BIT); + if (rc < 0) + dev_err(chg->dev, + "Couldn't configure AICL rc=%d\n", rc); + smblib_rerun_aicl(chg); + chg->charger_collpse = true; + schedule_delayed_work(&chg->op_re_set_work, + msecs_to_jiffies(TIME_1000MS)); + smblib_err(chg, "op_check_charger_collapse done\n"); + } +} + +static void op_check_charger_uovp(struct smb_charger *chg, int vchg_mv) +{ + static int over_volt_count, not_over_volt_count; + static bool uovp_satus, pre_uovp_satus; + int detect_time = 3; /* 3 x 6s = 18s */ + + if (!chg->vbus_present) + return; + + pr_debug("charger_voltage=%d charger_ovp=%d\n", vchg_mv, chg->chg_ovp); + + if (!chg->chg_ovp) { + if (vchg_mv > CHG_SOFT_OVP_MV || vchg_mv <= CHG_SOFT_UVP_MV) { + pr_err("charger is over voltage, count=%d\n", + over_volt_count); + uovp_satus = true; + if (pre_uovp_satus) + over_volt_count++; + else + over_volt_count = 0; + + pr_err("uovp_satus=%d,pre_uovp_satus=%d,over_volt_count=%d\n", + uovp_satus, pre_uovp_satus, over_volt_count); + if (detect_time <= over_volt_count) { + /* vchg continuous higher than 5.8v */ + pr_err("charger is over voltage, stop charging\n"); + op_charging_en(chg, false); + chg->chg_ovp = true; + } + } + } else { + if (vchg_mv < CHG_SOFT_OVP_MV - 100 + && vchg_mv > CHG_SOFT_UVP_MV + 100) { + uovp_satus = false; + if (!pre_uovp_satus) + not_over_volt_count++; + else + not_over_volt_count = 0; + + pr_err("uovp_satus=%d, pre_uovp_satus=%d,not_over_volt_count=%d\n", + uovp_satus, pre_uovp_satus, + not_over_volt_count); + if (detect_time <= not_over_volt_count) { + /* vchg continuous lower than 5.7v */ + pr_err("charger voltage is back to normal\n"); + op_charging_en(chg, true); + chg->chg_ovp = false; + op_check_battery_temp(chg); + smblib_rerun_aicl(chg); + } + } + } + pre_uovp_satus = uovp_satus; +} +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct smb_charger *chip = + container_of(self, struct smb_charger, fb_notif); + + if (evdata && evdata->data && chip) { + if (event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + if (!chip->oem_lcd_is_on) + set_property_on_fg(chip, + POWER_SUPPLY_PROP_UPDATE_LCD_IS_OFF, 0); + chip->oem_lcd_is_on = true; + } else if (*blank == FB_BLANK_POWERDOWN) { + if (chip->oem_lcd_is_on != false) + set_property_on_fg(chip, + POWER_SUPPLY_PROP_UPDATE_LCD_IS_OFF, 1); + chip->oem_lcd_is_on = false; + } + } + + } + + return 0; +} +#endif /*CONFIG_FB*/ + +#define SOFT_CHG_TERM_CURRENT 100 /* 100MA */ +void checkout_term_current(struct smb_charger *chg, int batt_temp) +{ + static int term_current_reached; + int current_ma, voltage_mv, temp_region, batt_status; + + batt_status = get_prop_batt_status(chg); + if (batt_status != POWER_SUPPLY_STATUS_CHARGING) + return; + + current_ma = get_prop_batt_current_now(chg) / 1000; + if (!(current_ma >= -SOFT_CHG_TERM_CURRENT + && current_ma <= SOFT_CHG_TERM_CURRENT)) { + /* soft charge term set to 100mA */ + term_current_reached = 0; + return; + } + + voltage_mv = get_prop_batt_voltage_now(chg) / 1000; + temp_region = op_battery_temp_region_get(chg); + if (voltage_mv >= chg->vbatmax[temp_region]) { + term_current_reached++; + } else { + term_current_reached = 0; + return; + } + + if (term_current_reached >= 5) { + chg->chg_done = true; + term_current_reached = 0; + pr_err("chg_done: CAP=%d (Q:%d), VBAT=%d (Q:%d), IBAT=%d (Q:%d), BAT_TEMP=%d\n", + get_prop_batt_capacity(chg), + get_prop_fg_capacity(chg), + get_prop_batt_voltage_now(chg) / 1000, + get_prop_fg_voltage_now(chg) / 1000, + get_prop_batt_current_now(chg) / 1000, + get_prop_fg_current_now(chg) / 1000, + get_prop_batt_temp(chg)); + op_charging_en(chg, false); + } +} + +/* xianglin add for usb enum at first bootup */ +static int usb_enum_check(const char *val, struct kernel_param *kp) +{ + const struct apsd_result *apsd_result; + unsigned long usb_sw_reset = 0; + int ret = 0; + struct smb_charger *chg = g_chg; + + /*if enum done, return */ + if (chg->usb_enum_status) + return 0; + + ret = kstrtoul(val, 10, &usb_sw_reset); + if (ret) + return ret; + + if (!usb_sw_reset) + return 0; + + if (!is_usb_present(chg)) + return 0; + /* if not SDP, return */ + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->bit != SDP_CHARGER_BIT + && apsd_result->bit != CDP_CHARGER_BIT) + return 0; + + pr_info("usb don't enum for longtime in boot\n"); + op_handle_usb_removal(chg); + chg->re_trigr_dash_done = true; + schedule_delayed_work( + &chg->non_standard_charger_check_work, + msecs_to_jiffies(TIME_1000MS)); + return 0; +} +module_param_call(sys_boot_complete, usb_enum_check, NULL, NULL, 0644); + +static void check_non_standard_charger_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, non_standard_charger_check_work); + + bool charger_present; + const struct apsd_result *apsd_result; + int aicl_result, rc; + + pr_debug("chg->non_stand_chg_count=%d\n", + chg->non_stand_chg_count); + + charger_present = is_usb_present(chg); + if (!charger_present) { + pr_info("chk_non_std_chger,charger_present\n"); + chg->non_stand_chg_count = 0; + return; + } + if (chg->usb_enum_status) { + pr_info("chk_non_std_chger,usb_enum_status\n"); + chg->non_stand_chg_count = 0; + return; + } + if (chg->non_stand_chg_count + >= NON_STANDARD_CHARGER_CHECK_S) { + apsd_result = smblib_update_usb_type(chg); + if (apsd_result->bit == DCP_CHARGER_BIT + || apsd_result->bit == OCP_CHARGER_BIT) + return; + rc = smblib_rerun_aicl(chg); + if (rc < 0) + smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc); + msleep(500); + aicl_result = op_get_aicl_result(chg); + chg->non_stand_chg_current = aicl_result; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + if (chg->is_aging_test) + op_usb_icl_set(chg, DEFAULT_AGAING_CHG_MA*1000); + else + op_usb_icl_set(chg, DEFAULT_DCP_MA*1000); + power_supply_changed(chg->batt_psy); + chg->is_power_changed = true; + chg->non_std_chg_present = true; + pr_err("non-standard_charger detected,aicl_result=%d\n", + aicl_result); + } else { + chg->non_stand_chg_count++; + schedule_delayed_work( + &chg->non_standard_charger_check_work, + msecs_to_jiffies(TIME_1000MS)); + } +} +static void smbchg_re_det_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + re_det_work.work); + + pr_debug("chg->redet_count=%d\n", chg->redet_count); + if (chg->usb_enum_status) { + pr_info("re_det, usb_enum_status\n"); + chg->redet_count = 0; + return; + } + if (!chg->vbus_present) { + pr_info("re_det, vbus_no_present\n"); + chg->redet_count = 0; + return; + } + + if (chg->redet_count >= REDET_COUTNT) { + op_rerun_apsd(chg); + chg->usb_type_redet_done = true; + } else { + chg->redet_count++; + schedule_delayed_work(&chg->re_det_work, + msecs_to_jiffies(TIME_1000MS)); + } +} +static void op_recovery_set_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, + struct smb_charger, + op_re_set_work.work); + int rc; + + pr_debug("chg->reset_count=%d\n", chg->reset_count); + if (!chg->charger_collpse) { + chg->reset_count = 0; + return; + } + if (!chg->vbus_present) { + chg->reset_count = 0; + return; + } + + if (chg->reset_count >= 13) { + + pr_err("op_set_collapse_fet\n"); + rc = smblib_write(chg, USBIN_AICL_OPTIONS_CFG_REG, 0xc7); + if (rc < 0) + smblib_err(chg, + "Couldn't enable OTG regulator rc=%d\n", rc); + chg->charger_collpse = false; + chg->reset_count = 0; + } else { + chg->reset_count++; + schedule_delayed_work(&chg->op_re_set_work, + msecs_to_jiffies(TIME_1000MS)); + } +} +void aging_test_check_aicl(struct smb_charger *chg) +{ + int aicl_result, vbat; + + if (chg->usb_enum_status) + return; + vbat = get_prop_fg_voltage_now(chg) / 1000; + aicl_result = op_get_aicl_result(chg); + if (aicl_result < 800*1000) { + if (vbat < 4000) { + pr_info("set icl 900mA\n"); + vote(chg->usb_icl_votable, AICL_RERUN_VOTER, + true, 900*1000); + vote(chg->usb_icl_votable, AICL_RERUN_VOTER, false, 0); + } + } +} +static void op_heartbeat_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct smb_charger *chg = container_of(dwork, + struct smb_charger, heartbeat_work); + enum temp_region_type temp_region; + bool charger_present; + bool fast_charging; + static int batt_temp, vbat_mv; + union power_supply_propval vbus_val; + int rc; +#ifndef CONFIG_OP_DEBUG_CHG + op_check_charge_timeout(chg); +#endif + rc = smblib_get_prop_usb_voltage_now(chg, &vbus_val); + if (rc < 0) { + pr_err("failed to read usb_voltage rc=%d\n", rc); + vbus_val.intval = CHG_VOLTAGE_NORMAL; + } + + charger_present = is_usb_present(chg); + if (!charger_present) + goto out; + + /* charger present */ + power_supply_changed(chg->batt_psy); + chg->dash_on = get_prop_fast_chg_started(chg); + if (chg->dash_on) { + switch_fast_chg(chg); + pr_info("fast chg started, usb_switch=%d\n", + op_is_usb_switch_on(chg)); + /* add for disable normal charge */ + fast_charging = op_get_fastchg_ing(chg); + if (fast_charging) { + if (!chg->disable_normal_chg_for_dash) + op_charging_en(chg, false); + chg->disable_normal_chg_for_dash = true; + } + goto out; + } else { + if (chg->disable_normal_chg_for_dash) { + chg->disable_normal_chg_for_dash = false; + op_charging_en(chg, true); + } + schedule_delayed_work(&chg->check_switch_dash_work, + msecs_to_jiffies(100)); + } + + op_check_charger_uovp(chg, vbus_val.intval); + op_check_battery_uovp(chg); + if (vbus_val.intval > 4500) + op_check_charger_collapse(chg); + + vbat_mv = get_prop_batt_voltage_now(chg) / 1000; + temp_region = op_battery_temp_region_get(chg); + if (temp_region == BATT_TEMP_LITTLE_COOL) { + if (vbat_mv > 4180 + 20 + && chg->temp_littel_cool_set_current_0_point_25c) { + chg->is_power_changed = true; + } else if (vbat_mv < 4180 - 10 + && !chg->temp_littel_cool_set_current_0_point_25c) { + chg->is_power_changed = true; + } + } + + batt_temp = get_prop_batt_temp(chg); + checkout_term_current(chg, batt_temp); + if (!chg->chg_ovp && chg->chg_done + && temp_region > BATT_TEMP_COLD + && temp_region < BATT_TEMP_HOT + && chg->vbatdet[temp_region] >= vbat_mv) { + chg->chg_done = false; + chg->recharge_pending = true; + chg->recharge_status = true; + + op_charging_en(chg, true); + pr_debug("temp_region=%d, recharge_pending\n", temp_region); + } + + if (!chg->chg_ovp && chg->battery_status == BATT_STATUS_GOOD + && !chg->time_out) { + op_check_battery_temp(chg); + } +#ifdef CONFIG_OP_DEBUG_CHG + chg->dump_count++; + if (chg->dump_count == 600) { + chg->dump_count = 0; + if ((get_prop_batt_current_now(chg) / 1000) > 0) { + op_dump_regs(chg); + aging_test_check_aicl(chg); + } + } +#else + if (chg->is_aging_test) { + chg->dump_count++; + if (chg->dump_count == 600) { + chg->dump_count = 0; + if ((get_prop_batt_current_now(chg) / 1000) > 0) { + op_dump_regs(chg); + aging_test_check_aicl(chg); + } + } + } + +#endif +out: + smblib_dbg(chg, PR_OP_DEBUG, "CAP=%d (Q:%d), VBAT=%d (Q:%d), IBAT=%d (Q:%d), BAT_TEMP=%d, CHG_TYPE=%d, VBUS=%d\n", + get_prop_batt_capacity(chg), + get_prop_fg_capacity(chg), + get_prop_batt_voltage_now(chg) / 1000, + get_prop_fg_voltage_now(chg) / 1000, + get_prop_batt_current_now(chg) / 1000, + get_prop_fg_current_now(chg) / 1000, + get_prop_batt_temp(chg), + chg->usb_psy_desc.type, + vbus_val.intval); + + /*update time 6s*/ + schedule_delayed_work(&chg->heartbeat_work, + round_jiffies_relative(msecs_to_jiffies + (HEARTBEAT_INTERVAL_MS))); +} + + +static int op_read(struct smb_charger *chg, u16 addr, u8 *val) +{ + unsigned int temp; + int rc = 0; + + if (pm_pon && pm_pon->regmap) { + rc = regmap_read(pm_pon->regmap, addr, &temp); + if (rc >= 0) + *val = (u8)temp; + } + return rc; +} + +int op_read_backup_flag(struct smb_charger *chg) +{ + u8 flag; + int rc = 0; + + rc = op_read(chg, SOC_DATA_REG_0, &flag); + if (rc) { + pr_err("failed to read PM addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + return 0; + } + flag = flag & BIT(7); + return flag; +} + +int op_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val) +{ + int rc = 0; + + if (pm_pon && pm_pon->regmap) { + mutex_lock(&chg->write_lock); + rc = regmap_update_bits(pm_pon->regmap, addr, mask, val); + mutex_unlock(&chg->write_lock); + } else { + pr_err("pm_pon is NULL\n"); + } + return rc; +} + +void op_write_backup_flag(struct smb_charger *chg, bool bk_flag) +{ + int rc = 0; + + rc = op_masked_write(chg, SOC_DATA_REG_0, + BIT(7), bk_flag ? BIT(7):0); + if (rc) { + pr_err("failed to clean PM addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + } +} + + +static int load_data(struct smb_charger *chg) +{ + u8 stored_soc = 0, flag; + int rc = 0, shutdown_soc = 0; + + if (!chg) { + pr_err("chg is NULL !\n"); + return SOC_INVALID; + } + flag = op_read_backup_flag(chg); + if (!flag) + return SOC_INVALID; + + rc = smblib_read(chg, SOC_DATA_REG_0, &stored_soc); + if (rc) { + pr_err("failed to read addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + return SOC_INVALID; + } + + /* the fist time connect battery, the PM 0x88d bit7 is 0,*/ + /* we do not need load this data.*/ + if (flag) + shutdown_soc = (stored_soc >> 1); + /* get data from bit1~bit7 */ + else + shutdown_soc = SOC_INVALID; + + pr_info("stored_soc[0x%x], shutdown_soc[%d]\n", + stored_soc, shutdown_soc); + return shutdown_soc; +} + +int load_soc(void) +{ + int soc = 0; + + soc = load_data(g_chg); + if (soc == SOC_INVALID || soc < 0 || soc > 100) + return -EINVAL; + return soc; +} + +static void clear_backup_soc(struct smb_charger *chg) +{ + int rc = 0; + u8 invalid_soc = SOC_INVALID; + + op_write_backup_flag(chg, false); + rc = smblib_masked_write(chg, SOC_DATA_REG_0, + BIT(7) | BIT(6) | BIT(5) | BIT(4) + | BIT(3) | BIT(2) | BIT(1), + (BIT(7) & invalid_soc) | (BIT(6) & invalid_soc) + | (BIT(5) & invalid_soc) | + (BIT(4) & invalid_soc) | (BIT(3) & invalid_soc) + | (BIT(2) & invalid_soc) | + (BIT(1) & invalid_soc)); + if (rc) + pr_err("failed to write addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + if (rc) { + pr_err("failed to clean PM addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + } +} + +void clean_backup_soc_ex(void) +{ + if (g_chg) + clear_backup_soc(g_chg); +} + +static void backup_soc(struct smb_charger *chg, int soc) +{ + int rc = 0; + + if (!chg || soc < 0 || soc > 100) { + pr_err("chg or soc invalid, store an invalid soc\n"); + if (chg) { + rc = smblib_masked_write(chg, SOC_DATA_REG_0, + BIT(7) | BIT(6) | BIT(5) + | BIT(4) | BIT(3) | BIT(2) | BIT(1), + BIT(7) | BIT(6) | BIT(5) + | BIT(4) | BIT(3) | BIT(2) | BIT(1)); + if (rc) + pr_err("failed to write addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + op_write_backup_flag(chg, false); + } + return; + } + + pr_err("backup_soc[%d]\n", soc); + soc = soc*2; + rc = smblib_masked_write(chg, SOC_DATA_REG_0, + BIT(7) | BIT(6) | BIT(5)| + BIT(4) | BIT(3) | BIT(2) | BIT(1), + (BIT(7) & soc) | (BIT(6) & soc) | (BIT(5) & soc) | + (BIT(4) & soc) | (BIT(3) & soc) | (BIT(2) & soc) | + (BIT(1) & soc)); + if (rc) + pr_err("failed to write addr[0x%x], rc=%d\n", + SOC_DATA_REG_0, rc); + op_write_backup_flag(chg, true); +} + +void backup_soc_ex(int soc) +{ + if (g_chg) + backup_soc(g_chg, soc); +} + +enum chg_protect_status_type { + PROTECT_CHG_OVP = 1, /* 1: VCHG > 5.8V */ + PROTECT_BATT_MISSING, /* 2: battery missing */ + PROTECT_CHG_OVERTIME, /* 3: charge overtime */ + PROTECT_BATT_OVP, /* 4: vbat >= 4.5 */ + PROTECT_BATT_TEMP_REGION__HOT,/* 5: 55 < t */ + PROTECT_BATT_TEMP_REGION_COLD,/* 6: t <= -3 */ + PROTECT_BATT_TEMP_REGION_LITTLE_COLD, /* 7: -3 < t <= 0 */ + PROTECT_BATT_TEMP_REGION_COOL,/* 8: 0 < t <= 5 */ + PROTECT_BATT_TEMP_REGION_WARM /* 9: 45 < t <= 55 */ +}; + +int get_prop_chg_protect_status(struct smb_charger *chg) +{ + int temp, rc; + bool batt_present; + enum temp_region_type temp_region; + union power_supply_propval vbus_val; + + if (chg->use_fake_protect_sts) + return chg->fake_protect_sts; + + if (!is_usb_present(chg)) + return 0; + + rc = smblib_get_prop_usb_voltage_now(chg, &vbus_val); + if (rc < 0) { + pr_err("failed to read usb_voltage rc=%d\n", rc); + vbus_val.intval = CHG_VOLTAGE_NORMAL; + } + + temp = get_prop_batt_temp(chg); + batt_present = get_prop_batt_present(chg); + temp_region = op_battery_temp_region_get(chg); + if (chg->chg_ovp && vbus_val.intval >= CHG_SOFT_OVP_MV - 100) + return PROTECT_CHG_OVP; + else if (!batt_present || BATT_REMOVE_TEMP > temp) + return PROTECT_BATT_MISSING; + else if (chg->battery_status == BATT_STATUS_BAD) + return PROTECT_BATT_OVP; + else if (true == chg->time_out) + return PROTECT_CHG_OVERTIME; + else if (temp_region == BATT_TEMP_HOT) + return PROTECT_BATT_TEMP_REGION__HOT; + else if (temp_region == BATT_TEMP_COLD) + return PROTECT_BATT_TEMP_REGION_COLD; + else if (temp_region == BATT_TEMP_LITTLE_COLD + && (chg->chg_done || chg->recharge_status)) + return PROTECT_BATT_TEMP_REGION_LITTLE_COLD; + else if (temp_region == BATT_TEMP_WARM + && (chg->chg_done || chg->recharge_status)) + return PROTECT_BATT_TEMP_REGION_WARM; + else + return 0; +} + +bool get_prop_fastchg_status(struct smb_charger *chg) +{ + int capacity; + + if (chg->dash_present) + return true; + + if (chg->hvdcp_present) { + capacity = get_prop_batt_capacity(chg); + if (capacity >= 1 && capacity <= 85) + return true; + } + + return false; +} + +static struct notify_dash_event notify_unplug_event = { + .notify_event = update_dash_unplug_status, + .op_contrl = op_contrl, + .notify_dash_charger_present + = set_dash_charger_present, +}; +void op_pm8998_regmap_register(struct qpnp_pon *pon) +{ + if (pm_pon) { + pm_pon = pon; + pr_err("multiple battery gauge called\n"); + } else { + pm_pon = pon; + } +} + +void fastcharge_information_register(struct external_battery_gauge *fast_chg) +{ + if (fast_charger) { + fast_charger = fast_chg; + pr_err("multiple battery gauge called\n"); + } else { + fast_charger = fast_chg; + } +} +EXPORT_SYMBOL(fastcharge_information_register); + +void fastcharge_information_unregister(struct external_battery_gauge *fast_chg) +{ + fast_charger = NULL; +} +EXPORT_SYMBOL(fastcharge_information_unregister); + +static int notify_usb_enumeration_function(int status) +{ + pr_info("status=%d\n", status); + g_chg->usb_enum_status = status; + + return g_chg->usb_enum_status; +} + +static struct notify_usb_enumeration_status usb_enumeration = { + .notify_usb_enumeration = notify_usb_enumeration_function, +}; + /************** * Additional USB PSY getters/setters * that call interrupt functions @@ -4536,6 +7000,7 @@ static void smblib_otg_oc_exit(struct smb_charger *chg, bool success) if (!success) { smblib_err(chg, "OTG soft start failed\n"); chg->otg_en = false; + vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0); } smblib_dbg(chg, PR_OTG, "enabling VBUS < 1V check\n"); @@ -4977,10 +7442,40 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->lock); mutex_init(&chg->write_lock); mutex_init(&chg->otg_oc_lock); + mutex_init(&chg->sw_dash_lock); mutex_init(&chg->vconn_oc_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work); INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work); + INIT_DELAYED_WORK(&chg->rechk_sw_dsh_work, retrigger_dash_work); + INIT_DELAYED_WORK(&chg->re_kick_work, op_re_kick_work); + INIT_DELAYED_WORK(&chg->op_check_apsd_work, op_chek_apsd_done_work); + INIT_DELAYED_WORK(&chg->recovery_suspend_work, + op_recovery_usb_suspend_work); + INIT_DELAYED_WORK(&chg->check_switch_dash_work, + op_check_allow_switch_dash_work); + INIT_DELAYED_WORK(&chg->heartbeat_work, + op_heartbeat_work); + INIT_DELAYED_WORK(&chg->non_standard_charger_check_work, + check_non_standard_charger_work); + INIT_DELAYED_WORK(&chg->re_det_work, smbchg_re_det_work); + INIT_DELAYED_WORK(&chg->op_re_set_work, op_recovery_set_work); + schedule_delayed_work(&chg->heartbeat_work, + msecs_to_jiffies(HEARTBEAT_INTERVAL_MS)); + notify_dash_unplug_register(¬ify_unplug_event); + wake_lock_init(&chg->chg_wake_lock, + WAKE_LOCK_SUSPEND, "chg_wake_lock"); + g_chg = chg; + + regsister_notify_usb_enumeration_status(&usb_enumeration); +#if defined(CONFIG_FB) + chg->fb_notif.notifier_call = fb_notifier_callback; + + rc = fb_register_client(&chg->fb_notif); + + if (rc) + pr_err("Unable to register fb_notifier: %d\n", rc); +#endif /*CONFIG_FB*/ INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_WORK(&chg->otg_oc_work, smblib_otg_oc_work); INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work); @@ -4988,6 +7483,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); + op_set_collapse_fet(chg, false); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); chg->fake_capacity = -EINVAL; @@ -5041,19 +7537,8 @@ int smblib_deinit(struct smb_charger *chg) { switch (chg->mode) { case PARALLEL_MASTER: - cancel_work_sync(&chg->bms_update_work); - cancel_work_sync(&chg->rdstd_cc2_detach_work); - cancel_delayed_work_sync(&chg->hvdcp_detect_work); - cancel_delayed_work_sync(&chg->clear_hdc_work); - cancel_work_sync(&chg->otg_oc_work); - cancel_work_sync(&chg->vconn_oc_work); - cancel_delayed_work_sync(&chg->otg_ss_done_work); - cancel_delayed_work_sync(&chg->icl_change_work); - cancel_delayed_work_sync(&chg->pl_enable_work); - cancel_work_sync(&chg->legacy_detection_work); - cancel_delayed_work_sync(&chg->uusb_otg_work); - cancel_delayed_work_sync(&chg->bb_removal_work); - power_supply_unreg_notifier(&chg->nb); + if (chg->nb.notifier_call) + power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); qcom_batt_deinit(); @@ -5067,5 +7552,6 @@ int smblib_deinit(struct smb_charger *chg) smblib_iio_deinit(chg); + notify_dash_unplug_unregister(¬ify_unplug_event); return 0; } diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 5ca5e923e651..b3826c6cec6f 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include "storm-watch.h" @@ -26,8 +28,23 @@ enum print_reason { PR_MISC = BIT(2), PR_PARALLEL = BIT(3), PR_OTG = BIT(4), + PR_OP_DEBUG = BIT(5), }; +#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER" +#define PSY_ICL_VOTER "PSY_ICL_VOTER" +#define TEMP_REGION_MAX 9 +#define NON_STANDARD_CHARGER_CHECK_S 90 +#define TIME_1000MS 1000 +#define REDET_COUTNT 5 +#define APSD_CHECK_COUTNT 15 +#define DASH_CHECK_COUNT 4 +#define BOOST_BACK_COUNT 8 +#define TIME_200MS 200 +#define TIME_100MS 100 + +#define TIME_3S 3000 + #define DEFAULT_VOTER "DEFAULT_VOTER" #define USER_VOTER "USER_VOTER" #define PD_VOTER "PD_VOTER" @@ -255,6 +272,7 @@ struct smb_charger { struct mutex ps_change_lock; struct mutex otg_oc_lock; struct mutex vconn_oc_lock; + struct mutex sw_dash_lock; /* power supplies */ struct power_supply *batt_psy; @@ -268,6 +286,9 @@ struct smb_charger { /* notifiers */ struct notifier_block nb; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#endif /* CONFIG_FB */ /* parallel charging */ struct parallel_params pl; @@ -301,6 +322,16 @@ struct smb_charger { struct work_struct rdstd_cc2_detach_work; struct delayed_work hvdcp_detect_work; struct delayed_work ps_change_timeout_work; + struct delayed_work rechk_sw_dsh_work; + struct delayed_work re_kick_work; + struct delayed_work recovery_suspend_work; + struct delayed_work check_switch_dash_work; + struct delayed_work non_standard_charger_check_work; + struct delayed_work heartbeat_work; + struct delayed_work re_det_work; + struct delayed_work op_re_set_work; + struct delayed_work op_check_apsd_work; + struct wake_lock chg_wake_lock; struct delayed_work clear_hdc_work; struct work_struct otg_oc_work; struct work_struct vconn_oc_work; @@ -312,6 +343,66 @@ struct smb_charger { struct delayed_work bb_removal_work; /* cached status */ + int BATT_TEMP_T0; + int BATT_TEMP_T1; + int BATT_TEMP_T2; + int BATT_TEMP_T3; + int BATT_TEMP_T4; + int BATT_TEMP_T5; + int BATT_TEMP_T6; + int batt_health; + int ibatmax[TEMP_REGION_MAX]; + int vbatmax[TEMP_REGION_MAX]; + int vbatdet[TEMP_REGION_MAX]; + int fake_chgvol; + int fake_temp; + int fake_protect_sts; + int non_stand_chg_current; + int non_stand_chg_count; + int redet_count; + int reset_count; + int dump_count; + int ck_apsd_count; + int ck_dash_count; + int recovery_boost_count; + + bool otg_switch; + bool use_fake_chgvol; + bool use_fake_temp; + bool use_fake_protect_sts; + bool vbus_present; + bool hvdcp_present; + bool dash_present; + bool charger_collpse; + bool usb_enum_status; + bool non_std_chg_present; + bool usb_type_redet_done; + bool time_out; + bool disable_normal_chg_for_dash; + bool ship_mode; + bool dash_on; + bool chg_ovp; + bool is_power_changed; + bool recharge_pending; + bool recharge_status; + bool temp_littel_cool_set_current_0_point_25c; + bool oem_lcd_is_on; + bool chg_enabled; + bool pd_disabled; + bool op_apsd_done; + bool re_trigr_dash_done; + bool boot_usb_present; + bool is_aging_test; + bool revert_boost_trigger; + enum temp_region_type mBattTempRegion; + enum batt_status_type battery_status; + short mBattTempBoundT0; + short mBattTempBoundT1; + short mBattTempBoundT2; + short mBattTempBoundT3; + short mBattTempBoundT4; + short mBattTempBoundT5; + short mBattTempBoundT6; int voltage_min_uv; int voltage_max_uv; int pd_active; @@ -368,6 +459,13 @@ struct smb_charger { int usb_icl_delta_ua; int pulse_cnt; }; +int smblib_set_prop_charge_parameter_set(struct smb_charger *chg); +extern void set_mcu_en_gpio_value(int value); +extern void usb_sw_gpio_set(int value); +extern bool op_set_fast_chg_allow(struct smb_charger *chg, bool enable); +extern bool get_prop_fast_chg_started(struct smb_charger *chg); +extern void mcu_en_gpio_set(int value); +extern void switch_mode_to_normal(void); int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val); @@ -440,6 +538,28 @@ int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val); +int get_prop_fast_adapter_update(struct smb_charger *chg); +void op_handle_usb_plugin(struct smb_charger *chg); +int op_rerun_apsd(struct smb_charger *chg); +irqreturn_t smblib_handle_aicl_done(int irq, void *data); +void op_charge_info_init(struct smb_charger *chg); +int update_dash_unplug_status(void); +int get_prop_batt_status(struct smb_charger *chg); +int get_prop_chg_protect_status(struct smb_charger *chg); +int op_set_prop_otg_switch(struct smb_charger *chg, + const union power_supply_propval *val); +int check_allow_switch_dash(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_chg_voltage(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_batt_temp(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_chg_protect_status(struct smb_charger *chg, + const union power_supply_propval *val); +bool op_get_fastchg_ing(struct smb_charger *chg); +bool get_prop_fastchg_status(struct smb_charger *chg); +int op_usb_icl_set(struct smb_charger *chg, int icl_ua); +int op_get_aicl_result(struct smb_charger *chg); int smblib_set_prop_input_current_limited(struct smb_charger *chg, const union power_supply_propval *val); diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c old mode 100644 new mode 100755 index a5dc1b983bcb..ec2f7c18de1b --- a/drivers/regulator/rpm-smd-regulator.c +++ b/drivers/regulator/rpm-smd-regulator.c @@ -1745,7 +1745,11 @@ static int rpm_vreg_device_probe(struct platform_device *pdev) init_data->constraints.valid_modes_mask |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; } - + pr_warn("reg->rdesc.name %s type %d\n", + init_data->constraints.name, + regulator_type); + if (regulator_type == RPM_REGULATOR_TYPE_BOB) + init_data->constraints.valid_modes_mask |= REGULATOR_MODE_FAST; reg->rdesc.name = init_data->constraints.name; reg->min_uV = init_data->constraints.min_uV; reg->max_uV = init_data->constraints.max_uV; diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c old mode 100644 new mode 100755 index bafcebb810de..a1a3251465ba --- a/drivers/rtc/qpnp-rtc.c +++ b/drivers/rtc/qpnp-rtc.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include /* RTC/ALARM Register offsets */ #define REG_OFFSET_ALARM_RW 0x40 @@ -87,6 +85,11 @@ static int qpnp_write_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val, u16 base, int count) { int rc; + if (base == (rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1)) { + dev_err(rtc_dd->rtc_dev, "write ALARM_CTRL1=0x%x\n", *rtc_val); + if (!(*rtc_val & BIT_RTC_ALARM_ENABLE)) + dump_stack(); + } rc = regmap_bulk_write(rtc_dd->regmap, base, rtc_val, count); if (rc) { @@ -335,7 +338,7 @@ qpnp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) rtc_dd->alarm_ctrl_reg1 = ctrl_reg; - dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", + dev_err(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", alarm->time.tm_hour, alarm->time.tm_min, alarm->time.tm_sec, alarm->time.tm_mday, alarm->time.tm_mon, alarm->time.tm_year); @@ -608,9 +611,6 @@ static int qpnp_rtc_probe(struct platform_device *pdev) goto fail_rtc_enable; } - /* Init power_on_alarm after adding rtc device */ - power_on_alarm_init(); - /* Request the alarm IRQ */ rc = request_any_context_irq(rtc_dd->rtc_alarm_irq, qpnp_alarm_trigger, IRQF_TRIGGER_RISING, @@ -655,6 +655,13 @@ static void qpnp_rtc_shutdown(struct platform_device *pdev) unsigned long irq_flags; struct qpnp_rtc *rtc_dd; bool rtc_alarm_powerup; + struct rtc_wkalrm alarm; + + qpnp_rtc_read_alarm(&pdev->dev, &alarm); + dev_err(&pdev->dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n", + alarm.time.tm_hour, alarm.time.tm_min, + alarm.time.tm_sec, alarm.time.tm_mday, + alarm.time.tm_mon, alarm.time.tm_year); if (!pdev) { pr_err("qpnp-rtc: spmi device not found\n"); @@ -668,7 +675,7 @@ static void qpnp_rtc_shutdown(struct platform_device *pdev) rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup; if (!rtc_alarm_powerup && !poweron_alarm) { spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags); - dev_dbg(&pdev->dev, "Disabling alarm interrupts\n"); + dev_err(&pdev->dev, "Disabling alarm interrupts\n"); /* Disable RTC alarms */ reg = rtc_dd->alarm_ctrl_reg1; diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c index 84765b17086c..0c862639fa3e 100644 --- a/drivers/scsi/ufs/ufs-qcom-ice.c +++ b/drivers/scsi/ufs/ufs-qcom-ice.c @@ -170,15 +170,17 @@ out: static void ufs_qcom_ice_cfg_work(struct work_struct *work) { unsigned long flags; + struct ice_data_setting ice_set; struct ufs_qcom_host *qcom_host = container_of(work, struct ufs_qcom_host, ice_cfg_work); + struct request *req_pending = NULL; if (!qcom_host->ice.vops->config_start) return; spin_lock_irqsave(&qcom_host->ice_work_lock, flags); - if (!qcom_host->req_pending) { - qcom_host->work_pending = false; + req_pending = qcom_host->req_pending; + if (!req_pending) { spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); return; } @@ -187,15 +189,24 @@ static void ufs_qcom_ice_cfg_work(struct work_struct *work) /* * config_start is called again as previous attempt returned -EAGAIN, * this call shall now take care of the necessary key setup. + * 'ice_set' will not actually be used, instead the next call to + * config_start() for this request, in the normal call flow, will + * succeed as the key has now been setup. */ qcom_host->ice.vops->config_start(qcom_host->ice.pdev, - qcom_host->req_pending, NULL, false); + qcom_host->req_pending, &ice_set, false); spin_lock_irqsave(&qcom_host->ice_work_lock, flags); qcom_host->req_pending = NULL; - qcom_host->work_pending = false; spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); + /* + * Resume with requests processing. We assume config_start has been + * successful, but even if it wasn't we still must resume in order to + * allow for the request to be retried. + */ + ufshcd_scsi_unblock_requests(qcom_host->hba); + } /** @@ -274,14 +285,18 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, * requires a non-atomic context, this means we should * call the function again from the worker thread to do * the configuration. For this request the error will - * propagate so it will be re-queued. + * propagate so it will be re-queued and until the + * configuration is is completed we block further + * request processing. */ if (err == -EAGAIN) { dev_dbg(qcom_host->hba->dev, "%s: scheduling task for ice setup\n", __func__); - if (!qcom_host->work_pending) { + if (!qcom_host->req_pending) { + ufshcd_scsi_block_requests( + qcom_host->hba); qcom_host->req_pending = cmd->request; if (!schedule_work( @@ -292,9 +307,10 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, &qcom_host->ice_work_lock, flags); + ufshcd_scsi_unblock_requests( + qcom_host->hba); return err; } - qcom_host->work_pending = true; } } else { @@ -393,7 +409,9 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, * requires a non-atomic context, this means we should * call the function again from the worker thread to do * the configuration. For this request the error will - * propagate so it will be re-queued. + * propagate so it will be re-queued and until the + * configuration is is completed we block further + * request processing. */ if (err == -EAGAIN) { @@ -401,8 +419,9 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, "%s: scheduling task for ice setup\n", __func__); - if (!qcom_host->work_pending) { - + if (!qcom_host->req_pending) { + ufshcd_scsi_block_requests( + qcom_host->hba); qcom_host->req_pending = cmd->request; if (!schedule_work( &qcom_host->ice_cfg_work)) { @@ -412,9 +431,10 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, &qcom_host->ice_work_lock, flags); + ufshcd_scsi_unblock_requests( + qcom_host->hba); return err; } - qcom_host->work_pending = true; } } else { diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index fd98a3381d61..8fd8cd7c89c6 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -375,7 +375,6 @@ struct ufs_qcom_host { struct work_struct ice_cfg_work; struct request *req_pending; struct ufs_vreg *vddp_ref_clk; - bool work_pending; }; static inline u32 diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index e4b2c95350ef..13c1a4e7fe5b 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -154,7 +154,10 @@ enum ufs_desc_max_size { * of descriptor header. */ QUERY_DESC_STRING_MAX_SIZE = 0xFE, - QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x44, + /* according the UFS2.1 spec, + * this value had changed from 0x44 to 0x48 + */ + QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x48, QUERY_DESC_POWER_MAX_SIZE = 0x62, QUERY_DESC_RFU_MAX_SIZE = 0x00, }; diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c index 7a501d6d7c84..fe0d797e5cb5 100644 --- a/drivers/scsi/ufs/ufs_quirks.c +++ b/drivers/scsi/ufs/ufs_quirks.c @@ -14,6 +14,7 @@ #include "ufshcd.h" #include "ufs_quirks.h" +#include static struct ufs_card_fix ufs_fixups[] = { /* UFS cards deviations table */ @@ -48,6 +49,105 @@ static struct ufs_card_fix ufs_fixups[] = { END_FIX }; +static int ufs_get_capacity_info(struct ufs_hba *hba, u64 *pcapacity) +{ + int err; + u8 geometry_buf[QUERY_DESC_GEOMETRY_MAZ_SIZE]; + + err = ufshcd_read_geometry_desc(hba, geometry_buf, + QUERY_DESC_GEOMETRY_MAZ_SIZE); + if (err) + goto out; + + *pcapacity = (u64)geometry_buf[0x04] << 56 | + (u64)geometry_buf[0x04 + 1] << 48 | + (u64)geometry_buf[0x04 + 2] << 40 | + (u64)geometry_buf[0x04 + 3] << 32 | + (u64)geometry_buf[0x04 + 4] << 24 | + (u64)geometry_buf[0x04 + 5] << 16 | + (u64)geometry_buf[0x04 + 6] << 8 | + (u64)geometry_buf[0x04 + 7]; + + printk("ufs_get_capacity_info size = 0x%llx", *pcapacity); + +out: + return err; +} + +static char* ufs_get_capacity_size(u64 capacity) +{ + if (capacity == 0x1D62000)/*16G*/ + return "16G"; + else if (capacity == 0x3B9E000)/*32G*/ + return "32G"; + else if (capacity == 0x7734000)/*64G*/ + return "64G"; + else if (capacity == 0xEE60000)/*128G*/ + return "128G"; + else if (capacity == 0xEE64000)/*128G V4*/ + return "128G"; + else + return "0G"; +} + +char ufs_vendor_and_rev[32] = {'\0'}; +char ufs_product_id[32] = {'\0'}; +int ufs_fill_info(struct ufs_hba *hba) +{ + int err=0; + u64 ufs_capacity = 0; + char ufs_vendor[9]={'\0'}; + char ufs_rev[5]={'\0'}; + + /* Error Handle: Before filling ufs info, we must confirm sdev_ufs_device structure is not NULL*/ + if(!hba->sdev_ufs_device) { + dev_err(hba->dev, "%s:hba->sdev_ufs_device is NULL!\n", __func__); + goto out; + } + + /* Copy UFS info from host controller structure (ex:vendor name, firmware revision) */ + if(!hba->sdev_ufs_device->vendor) { + dev_err(hba->dev, "%s: UFS vendor info is NULL\n", __func__); + strlcpy(ufs_vendor, "UNKNOWN", 7); + } else { + strlcpy(ufs_vendor, hba->sdev_ufs_device->vendor, + sizeof(ufs_vendor)-1); + } + + if(!hba->sdev_ufs_device->rev) { + dev_err(hba->dev, "%s: UFS firmware info is NULL\n", __func__); + strncpy(ufs_rev, "UNKNOWN", 7); + } else { + strncpy(ufs_rev, hba->sdev_ufs_device->rev, sizeof(ufs_rev)-1); + } + + if(!hba->sdev_ufs_device->model) { + dev_err(hba->dev, "%s: UFS product id info is NULL\n", __func__); + strlcpy(ufs_product_id, "UNKNOWN", 7); + } else { + strlcpy(ufs_product_id, hba->sdev_ufs_device->model, 16); + } + + /* Get UFS storage size*/ + err = ufs_get_capacity_info(hba, &ufs_capacity); + if (err) { + dev_err(hba->dev, "%s: Failed getting capacity info\n", __func__); + goto out; + } + + /* Combine vendor name with firmware revision */ + strcat(ufs_vendor_and_rev, ufs_vendor); + strcat(ufs_vendor_and_rev, " "); + strcat(ufs_vendor_and_rev, ufs_get_capacity_size(ufs_capacity)); + strcat(ufs_vendor_and_rev, " "); + strcat(ufs_vendor_and_rev, ufs_rev); + + push_component_info(UFS, ufs_product_id, ufs_vendor_and_rev); +out: + return err; + +} + static int ufs_get_device_info(struct ufs_hba *hba, struct ufs_card_info *card_data) { diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 3102517e841c..8266eaa574a0 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -149,4 +149,6 @@ struct ufs_card_fix { struct ufs_hba; void ufs_advertise_fixup_device(struct ufs_hba *hba); + +int ufs_fill_info(struct ufs_hba *hba); #endif /* UFS_QUIRKS_H_ */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6af0ca6eb7e2..e8139ce6a072 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -611,7 +611,7 @@ static void ufshcd_print_cmd_log(struct ufs_hba *hba) p = &hba->cmd_log.entries[pos]; pos = (pos + 1) % UFSHCD_MAX_CMD_LOGGING; - if (ktime_to_us(p->tstamp)) { + /*if (ktime_to_us(p->tstamp)) { pr_err("%s: %s: seq_no=%u lun=0x%x cmd_id=0x%02x lba=0x%llx txfer_len=%d tag=%u, doorbell=0x%x outstanding=0x%x idn=%d time=%lld us\n", p->cmd_type, p->str, p->seq_num, p->lun, p->cmd_id, (unsigned long long)p->lba, @@ -620,6 +620,7 @@ static void ufshcd_print_cmd_log(struct ufs_hba *hba) ktime_to_us(p->tstamp)); usleep_range(1000, 1100); } + */ } } #else @@ -3815,6 +3816,12 @@ int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size); } + +int ufshcd_read_geometry_desc(struct ufs_hba *hba, u8 *buf, u32 size) +{ + return ufshcd_read_desc(hba, QUERY_DESC_IDN_GEOMETRY, 0, buf, size); +} + /** * ufshcd_read_string_desc - read string descriptor * @hba: pointer to adapter instance @@ -5410,12 +5417,14 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) result = DID_ERROR << 16; dev_err(hba->dev, "Reject UPIU not fully implemented\n"); + WARN_ON(1); break; default: result = DID_ERROR << 16; dev_err(hba->dev, "Unexpected request response code = %x\n", result); + WARN_ON(1); break; } break; @@ -5446,6 +5455,7 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) */ __ufshcd_print_host_regs(hba, true); ufshcd_print_host_state(hba); + WARN_ON(1); break; } /* end of switch */ @@ -7586,6 +7596,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } hba->clk_scaling.is_allowed = true; } + ufs_fill_info(hba); scsi_scan_host(hba->host); pm_runtime_put_sync(hba->dev); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 931b6b31de19..b63667d0666b 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -1160,6 +1160,8 @@ out: int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size); +int ufshcd_read_geometry_desc(struct ufs_hba *hba, u8 *buf, u32 size); + static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) { return (pwr_info->pwr_rx == FAST_MODE || diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 62b7d12629e4..e27c4dba6a48 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -945,6 +945,14 @@ config QCOM_EARLY_RANDOM may not be truly random. Select this option to make an early call to get some random data to put in the pool. If unsure, say N. +config RF_CABLE_DETECT + bool "detect RF cable connection" + help + detect RF cable connection for different RF configuration + To compile this driver as a module, choose M here: the module + will be called RF cable. + If unsure, say N. + config QCOM_CX_IPEAK bool "Common driver to handle Cx iPeak limitation" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 5e565c863889..eeb5cba4325b 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -106,6 +106,8 @@ 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 obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o +obj-y += project_info.o +obj-$(CONFIG_RF_CABLE_DETECT) += op_rf_cable_monitor.o obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o obj-$(CONFIG_QCOM_CX_IPEAK) += cx_ipeak.o obj-$(CONFIG_MSM_CACHE_M4M_ERP64) += cache_m4m_erp64.o diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c old mode 100644 new mode 100755 index d56fb48b0d43..f9b4bb6fa3d6 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -48,6 +48,8 @@ #include #include #include +#include +static u32 fw_version; #include "wlan_firmware_service_v01.h" @@ -4394,6 +4396,24 @@ static int icnss_get_vbatt_info(struct icnss_priv *priv) return 0; } +/* Initial and show wlan firmware build version */ +void cnss_set_fw_version(u32 version) { + fw_version = version; +} +EXPORT_SYMBOL(cnss_set_fw_version); + +static ssize_t cnss_version_information_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!penv) + return -ENODEV; + return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u\n", (fw_version & 0xf0000000) >> 28, + (fw_version & 0xf000000) >> 24, (fw_version & 0xf00000) >> 20, fw_version & 0x7fff); +} + +static DEVICE_ATTR(cnss_version_information, 0444, + cnss_version_information_show, NULL); + static int icnss_probe(struct platform_device *pdev) { int ret = 0; @@ -4590,6 +4610,10 @@ static int icnss_probe(struct platform_device *pdev) penv = priv; + /* Create device file */ + device_create_file(&penv->pdev->dev, &dev_attr_cnss_version_information); + push_component_info(WCN, "WCN3990", "QualComm"); + icnss_pr_info("Platform driver probed successfully\n"); return 0; @@ -4612,6 +4636,8 @@ static int icnss_remove(struct platform_device *pdev) icnss_debugfs_destroy(penv); + device_remove_file(&penv->pdev->dev, &dev_attr_cnss_version_information); + icnss_modem_ssr_unregister_notifier(penv); destroy_ramdump_device(penv->msa0_dump_dev); diff --git a/drivers/soc/qcom/msm_smem.c b/drivers/soc/qcom/msm_smem.c old mode 100644 new mode 100755 index cf3e0e084ab4..d13f69b30686 --- a/drivers/soc/qcom/msm_smem.c +++ b/drivers/soc/qcom/msm_smem.c @@ -539,13 +539,16 @@ void *smem_find(unsigned id, unsigned size_in, unsigned to_proc, unsigned flags) return ERR_PTR(-EPROBE_DEFER); ptr = smem_get_entry(id, &size, to_proc, flags); - if (!ptr) + if (!ptr) { + pr_err("smem_get_entry return null\n"); return 0; - + } size_in = ALIGN(size_in, 8); if (size_in != size) { SMEM_INFO("smem_find(%u, %u, %u, %u): wrong size %u\n", id, size_in, to_proc, flags, size); + pr_err("smem_find(%u, %u, %u, %u): wrong size %u\n", + id, size_in, to_proc, flags, size); return 0; } diff --git a/drivers/soc/qcom/op_rf_cable_monitor.c b/drivers/soc/qcom/op_rf_cable_monitor.c new file mode 100644 index 000000000000..9c3aa9b72acb --- /dev/null +++ b/drivers/soc/qcom/op_rf_cable_monitor.c @@ -0,0 +1,296 @@ +/*For OEM project monitor RF cable connection status, +*and config different RF configuration +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct project_info *project_info_desc; + +struct cable_data { + int irq; + int cable_gpio; + struct delayed_work work; + struct workqueue_struct *wqueue; + struct device *dev; + struct wake_lock wl; + atomic_t running; + atomic_t pre_state; + spinlock_t lock; + int enable; +}; +static struct cable_data *_cdata; +static DEFINE_MUTEX(sem); + +static char *cmdline_find_option(char *str) +{ + return strnstr(saved_command_line, str, strlen(saved_command_line)); +} + +int modify_rf_cable_smem_info(uint32 status) +{ + project_info_desc = smem_find(SMEM_PROJECT_INFO, + sizeof(struct project_info), 0, + SMEM_ANY_HOST_FLAG); + + if (IS_ERR_OR_NULL(project_info_desc)) + pr_err("%s: get project_info failure\n", __func__); + else { + project_info_desc->rf_v3 = status; + pr_err("%s: rf_cable: %d\n", + __func__, project_info_desc->rf_v3); + } + return 0; +} + +static void rf_cable_work(struct work_struct *work) +{ + unsigned long flags; + + spin_lock_irqsave(&_cdata->lock, flags); + disable_irq_nosync(_cdata->irq); + spin_unlock_irqrestore(&_cdata->lock, flags); + if (atomic_read(&_cdata->running) == 0) { + atomic_set(&_cdata->running, 1); + msleep(300); + if (atomic_read(&_cdata->pre_state) != + gpio_get_value(_cdata->cable_gpio)) { + atomic_set(&_cdata->pre_state, + gpio_get_value(_cdata->cable_gpio)); + modify_rf_cable_smem_info( + gpio_get_value(_cdata->cable_gpio)); + op_restart_modem(); + } + } + spin_lock_irqsave(&_cdata->lock, flags); + enable_irq(_cdata->irq); + atomic_set(&_cdata->running, 0); + spin_unlock_irqrestore(&_cdata->lock, flags); +} + +irqreturn_t cable_interrupt(int irq, void *_dev) +{ + wake_lock_timeout(&_cdata->wl, + msecs_to_jiffies(CABLE_WAKELOCK_HOLD_TIME)); + queue_delayed_work(_cdata->wqueue, + &_cdata->work, msecs_to_jiffies(1)); + return IRQ_HANDLED; +} + +static ssize_t rf_cable_proc_read_func(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char page[PAGESIZE]; + + ret = snprintf(page, sizeof(page[PAGESIZE]), "%d\n", _cdata->enable); + ret = simple_read_from_buffer(user_buf, + count, ppos, page, strlen(page)); + return ret; +} + +static ssize_t rf_cable_proc_write_func(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + unsigned long flags; + int enable = 0; + int rc; + + rc = kstrtoint(buffer, 0, &enable); + if (rc != 0) + return -EINVAL; + + pr_err("%s: cable_enable (%d)\n", __func__, enable); + if (enable != _cdata->enable) { + _cdata->enable = enable; + if (!_cdata->enable) { + spin_lock_irqsave(&_cdata->lock, flags); + disable_irq_nosync(_cdata->irq); + spin_unlock_irqrestore(&_cdata->lock, flags); + pr_err("%s: pre_state (%d)\n", + __func__, atomic_read(&_cdata->pre_state)); + if (atomic_read(&_cdata->pre_state) != 1) { + atomic_set(&_cdata->pre_state, 1); + modify_rf_cable_smem_info(1); + op_restart_modem(); + } + } else { + spin_lock_irqsave(&_cdata->lock, flags); + enable_irq(_cdata->irq); + spin_unlock_irqrestore(&_cdata->lock, flags); + pr_err("%s: pre_state (%d)\n", + __func__, atomic_read(&_cdata->pre_state)); + if (atomic_read(&_cdata->pre_state) != + gpio_get_value(_cdata->cable_gpio)) { + atomic_set(&_cdata->pre_state, + gpio_get_value(_cdata->cable_gpio)); + modify_rf_cable_smem_info( + gpio_get_value(_cdata->cable_gpio)); + op_restart_modem(); + } + } + } + return count; +} + +static const struct file_operations rf_enable_proc_fops = { + .write = rf_cable_proc_write_func, + .read = rf_cable_proc_read_func, + .open = simple_open, + .owner = THIS_MODULE, +}; + +int create_rf_cable_procfs(void) +{ + int ret = 0; + + if (!proc_create("rf_cable_config", + 0666, NULL, &rf_enable_proc_fops)) { + pr_err("%s: proc_create enable fail!\n", __func__); + ret = -1; + } + _cdata->enable = 1; + return ret; +} + +static int op_rf_request_named_gpio(const char *label, int *gpio) +{ + struct device *dev = _cdata->dev; + struct device_node *np = dev->of_node; + int rc = of_get_named_gpio(np, label, 0); + + if (rc < 0) { + dev_err(dev, "failed to get '%s'\n", label); + *gpio = rc; + return rc; + } + *gpio = rc; + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + dev_info(dev, "%s - gpio: %d\n", label, *gpio); + return 0; +} + +static int op_rf_cable_probe(struct platform_device *pdev) +{ + int rc = 0; + struct device *dev = &pdev->dev; + + if (cmdline_find_option("ftm_mode")) { + pr_err("%s: ftm_mode FOUND! use 1 always\n", __func__); + modify_rf_cable_smem_info(1); + } else { + _cdata = kzalloc(sizeof(struct cable_data), GFP_KERNEL); + if (!_cdata) { + pr_err("%s: failed to allocate memory\n", __func__); + rc = -ENOMEM; + goto exit; + } + + _cdata->dev = dev; + dev_set_drvdata(dev, _cdata); + + rc = op_rf_request_named_gpio("rf,cable-gpio", + &_cdata->cable_gpio); + if (rc) { + pr_err("%s: op_rf_request_named_gpio fail\n", __func__); + goto exit_gpio; + } + gpio_direction_input(_cdata->cable_gpio); + + _cdata->wqueue = create_singlethread_workqueue( + "op_rf_cable_wqueue"); + INIT_DELAYED_WORK(&_cdata->work, rf_cable_work); + _cdata->irq = gpio_to_irq(_cdata->cable_gpio); + if (_cdata->irq < 0) { + pr_err("Unable to get irq number for GPIO %d, error %d\n", + _cdata->cable_gpio, _cdata->irq); + rc = _cdata->irq; + goto exit_gpio; + } + + rc = request_irq(_cdata->irq, cable_interrupt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "op_rf_cable", _cdata); + if (rc) { + pr_err("could not request irq %d\n", _cdata->irq); + goto exit_gpio; + } + pr_err("requested irq %d\n", _cdata->irq); + enable_irq_wake(_cdata->irq); + + wake_lock_init(&_cdata->wl, WAKE_LOCK_SUSPEND, + "rf_cable_wake_lock"); + spin_lock_init(&_cdata->lock); + atomic_set(&_cdata->running, 0); + atomic_set(&_cdata->pre_state, + gpio_get_value(_cdata->cable_gpio)); + + modify_rf_cable_smem_info( + gpio_get_value(_cdata->cable_gpio)); + create_rf_cable_procfs(); + } + pr_err("%s: probe ok!\n", __func__); + return 0; + +exit_gpio: + kfree(_cdata); +exit: + pr_err("%s: probe Fail!\n", __func__); + + return rc; +} + +static const struct of_device_id rf_of_match[] = { + { .compatible = "oem,rf_cable", }, + {} +}; +MODULE_DEVICE_TABLE(of, rf_of_match); + +static struct platform_driver op_rf_cable_driver = { + .driver = { + .name = "op_rf_cable", + .owner = THIS_MODULE, + .of_match_table = rf_of_match, + }, + .probe = op_rf_cable_probe, +}; + +static int __init op_rf_cable_init(void) +{ + int ret; + + ret = platform_driver_register(&op_rf_cable_driver); + if (ret) + pr_err("rf_cable_driver register failed: %d\n", ret); + + return ret; +} + +MODULE_LICENSE("GPL v2"); +subsys_initcall(op_rf_cable_init); diff --git a/drivers/soc/qcom/project_info.c b/drivers/soc/qcom/project_info.c new file mode 100644 index 000000000000..dbe2eb4f162c --- /dev/null +++ b/drivers/soc/qcom/project_info.c @@ -0,0 +1,613 @@ +/*For OEM project information +*such as project name, hardware ID +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct component_info component_info_desc[COMPONENT_MAX]; +static struct kobject *project_info_kobj; +static struct project_info *project_info_desc; +static struct kobject *component_info; +static ssize_t project_info_get(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t component_info_get(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(project_name, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(hw_id, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v1, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v2, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(rf_id_v3, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(modem, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(operator_no, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(ddr_manufacture_info, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(ddr_row, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(ddr_column, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(ddr_fw_version, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(ddr_reserve_info, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(secboot_status, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(platform_id, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(serialno, S_IRUGO, project_info_get, NULL); +static DEVICE_ATTR(feature_id, S_IRUGO, project_info_get, NULL); + +uint8 get_secureboot_fuse_status(void) +{ + void __iomem *oem_config_base; + uint8 secure_oem_config = 0; + + oem_config_base = ioremap(SECURE_BOOT1, 1); + if (!oem_config_base) + return -EINVAL; + secure_oem_config = __raw_readb(oem_config_base); + iounmap(oem_config_base); + pr_debug("secure_oem_config 0x%x\n", secure_oem_config); + + return secure_oem_config; +} + +static ssize_t project_info_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (project_info_desc) { + if (attr == &dev_attr_project_name) + return snprintf(buf, BUF_SIZE, "%s\n", + project_info_desc->project_name); + if (attr == &dev_attr_hw_id) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->hw_version); + if (attr == &dev_attr_rf_id_v1) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->rf_v1); + if (attr == &dev_attr_rf_id_v2) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->rf_v2); + if (attr == &dev_attr_rf_id_v3) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->rf_v3); + if (attr == &dev_attr_modem) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->modem); + if (attr == &dev_attr_operator_no) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->operator); + if (attr == &dev_attr_ddr_manufacture_info) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->ddr_manufacture_info); + if (attr == &dev_attr_ddr_row) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->ddr_row); + if (attr == &dev_attr_ddr_column) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->ddr_column); + if (attr == &dev_attr_ddr_fw_version) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->ddr_fw_version); + if (attr == &dev_attr_ddr_reserve_info) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->ddr_reserve_info); + if (attr == &dev_attr_secboot_status) + return snprintf(buf, BUF_SIZE, "%d\n", + get_secureboot_fuse_status()); + if (attr == &dev_attr_platform_id) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->platform_id); + if (attr == &dev_attr_serialno) + return snprintf(buf, BUF_SIZE, "0x%x\n", + chip_serial_num); + if (attr == &dev_attr_feature_id) + return snprintf(buf, BUF_SIZE, "%d\n", + project_info_desc->feature_id); + } + + return -EINVAL; +} + +static struct attribute *project_info_sysfs_entries[] = { + &dev_attr_project_name.attr, + &dev_attr_hw_id.attr, + &dev_attr_rf_id_v1.attr, + &dev_attr_rf_id_v2.attr, + &dev_attr_rf_id_v3.attr, + &dev_attr_modem.attr, + &dev_attr_operator_no.attr, + &dev_attr_ddr_manufacture_info.attr, + &dev_attr_ddr_row.attr, + &dev_attr_ddr_column.attr, + &dev_attr_ddr_fw_version.attr, + &dev_attr_ddr_reserve_info.attr, + &dev_attr_secboot_status.attr, + &dev_attr_platform_id.attr, + &dev_attr_serialno.attr, + &dev_attr_feature_id.attr, + NULL, +}; + +static struct attribute_group project_info_attr_group = { + .attrs = project_info_sysfs_entries, +}; + +static DEVICE_ATTR(ddr, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(emmc, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(f_camera, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(r_camera, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(second_r_camera, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(tp, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(lcd, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(wcn, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(l_sensor, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(g_sensor, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(m_sensor, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(gyro, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(backlight, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(mainboard, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(fingerprints, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(touch_key, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(ufs, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(Aboard, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(nfc, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(fast_charge, S_IRUGO, component_info_get, NULL); +static DEVICE_ATTR(cpu, S_IRUGO, component_info_get, NULL); + +char *get_component_version(enum COMPONENT_TYPE type) +{ + if (type >= COMPONENT_MAX) { + pr_err("%s == type %d invalid\n", __func__, type); + return "N/A"; + } + return component_info_desc[type].version?:"N/A"; +} + +char *get_component_manufacture(enum COMPONENT_TYPE type) +{ + if (type >= COMPONENT_MAX) { + pr_err("%s == type %d invalid\n", __func__, type); + return "N/A"; + } + return component_info_desc[type].manufacture?:"N/A"; + +} + +int push_component_info(enum COMPONENT_TYPE type, + char *version, char *manufacture) +{ + if (type >= COMPONENT_MAX) + return -ENOMEM; + component_info_desc[type].version = version; + component_info_desc[type].manufacture = manufacture; + + return 0; +} +EXPORT_SYMBOL(push_component_info); + +int reset_component_info(enum COMPONENT_TYPE type) +{ + if (type >= COMPONENT_MAX) + return -ENOMEM; + component_info_desc[type].version = NULL; + component_info_desc[type].manufacture = NULL; + + return 0; +} +EXPORT_SYMBOL(reset_component_info); + + +static struct attribute *component_info_sysfs_entries[] = { + &dev_attr_ddr.attr, + &dev_attr_emmc.attr, + &dev_attr_f_camera.attr, + &dev_attr_r_camera.attr, + &dev_attr_second_r_camera.attr, + &dev_attr_tp.attr, + &dev_attr_lcd.attr, + &dev_attr_wcn.attr, + &dev_attr_l_sensor.attr, + &dev_attr_g_sensor.attr, + &dev_attr_m_sensor.attr, + &dev_attr_gyro.attr, + &dev_attr_backlight.attr, + &dev_attr_mainboard.attr, + &dev_attr_fingerprints.attr, + &dev_attr_touch_key.attr, + &dev_attr_ufs.attr, + &dev_attr_Aboard.attr, + &dev_attr_nfc.attr, + &dev_attr_fast_charge.attr, + &dev_attr_cpu.attr, + NULL, +}; + +static struct attribute_group component_info_attr_group = { + .attrs = component_info_sysfs_entries, +}; + +static ssize_t component_info_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (attr == &dev_attr_ddr) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(DDR), + get_component_manufacture(DDR)); + if (attr == &dev_attr_emmc) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(EMMC), + get_component_manufacture(EMMC)); + if (attr == &dev_attr_f_camera) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(F_CAMERA), + get_component_manufacture(F_CAMERA)); + if (attr == &dev_attr_r_camera) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(R_CAMERA), + get_component_manufacture(R_CAMERA)); + if (attr == &dev_attr_second_r_camera) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(SECOND_R_CAMERA), + get_component_manufacture(SECOND_R_CAMERA)); + if (attr == &dev_attr_tp) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(TP), + get_component_manufacture(TP)); + if (attr == &dev_attr_lcd) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(LCD), + get_component_manufacture(LCD)); + if (attr == &dev_attr_wcn) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(WCN), + get_component_manufacture(WCN)); + if (attr == &dev_attr_l_sensor) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(I_SENSOR), + get_component_manufacture(I_SENSOR)); + if (attr == &dev_attr_g_sensor) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(G_SENSOR), + get_component_manufacture(G_SENSOR)); + if (attr == &dev_attr_m_sensor) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(M_SENSOR), + get_component_manufacture(M_SENSOR)); + if (attr == &dev_attr_gyro) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(GYRO), + get_component_manufacture(GYRO)); + if (attr == &dev_attr_backlight) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(BACKLIGHT), + get_component_manufacture(BACKLIGHT)); + if (attr == &dev_attr_mainboard) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(MAINBOARD), + get_component_manufacture(MAINBOARD)); + if (attr == &dev_attr_fingerprints) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(FINGERPRINTS), + get_component_manufacture(FINGERPRINTS)); + if (attr == &dev_attr_touch_key) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(TOUCH_KEY), + get_component_manufacture(TOUCH_KEY)); + if (attr == &dev_attr_ufs) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(UFS), + get_component_manufacture(UFS)); + if (attr == &dev_attr_Aboard) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(ABOARD), + get_component_manufacture(ABOARD)); + if (attr == &dev_attr_nfc) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(NFC), + get_component_manufacture(NFC)); + if (attr == &dev_attr_fast_charge) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(FAST_CHARGE), + get_component_manufacture(FAST_CHARGE)); + if (attr == &dev_attr_cpu) + return snprintf(buf, BUF_SIZE, "VER:\t%s\nMANU:\t%s\n", + get_component_version(CPU), + get_component_manufacture(CPU)); + return -EINVAL; +} + +static int __init project_info_init_sysfs(void) +{ + int error = 0; + + project_info_kobj = kobject_create_and_add("project_info", NULL); + if (!project_info_kobj) + return -ENOMEM; + error = sysfs_create_group(project_info_kobj, &project_info_attr_group); + if (error) { + pr_err("project_info_init_sysfs project_info_attr_group failure\n"); + return error; + } + + component_info = kobject_create_and_add("component_info", + project_info_kobj); + pr_info("project_info_init_sysfs success\n"); + if (!component_info) + return -ENOMEM; + + error = sysfs_create_group(component_info, &component_info_attr_group); + if (error) { + pr_err("project_info_init_sysfs project_info_attr_group failure\n"); + return error; + } + return 0; +} + +late_initcall(project_info_init_sysfs); + +struct ddr_manufacture { + int id; + char name[20]; +}; + +static char ddr_version[32] = {0}; +static char ddr_manufacture[20] = {0}; +char ddr_manufacture_and_fw_verion[40] = {0}; +static char cpu_type[20] = {0}; + +struct ddr_manufacture ddr_manufacture_list[] = { + {1, "Samsung "}, + {2, "Qimonda "}, + {3, "Elpida "}, + {4, "Etpon "}, + {5, "Nanya "}, + {6, "Hynix "}, + {7, "Mosel "}, + {8, "Winbond "}, + {9, "Esmt "}, + {255, "Micron"}, + {0, "Unknown"}, +}; + +struct cpu_list { + int id; + char name[20]; +}; + +struct cpu_list cpu_list_msm[] = { + {194, "MSM8974AC "}, + {217, "MSM8974AA "}, + {218, "MSM8974AB "}, + {207, "MSM8994 "}, + {246, "MSM8996 "}, + {292, "MSM8998 "}, + {302, "MSM8996L "}, + {305, "MSM8996SG "}, + {310, "MSM8996AU "}, + {0, "Unknown"}, +}; + +void get_ddr_manufacture_name(void) +{ + uint32 i, length; + + length = ARRAY_SIZE(ddr_manufacture_list); + if (project_info_desc) { + for (i = 0; i < length; i++) { + if (ddr_manufacture_list[i].id == + project_info_desc->ddr_manufacture_info) { + snprintf(ddr_manufacture, BUF_SIZE, "%s", + ddr_manufacture_list[i].name); + break; + } + } + } +} + +void get_cpu_type(void) +{ + uint32 i, length; + + length = ARRAY_SIZE(cpu_list_msm); + if (project_info_desc) { + for (i = 0; i < length; i++) { + if (cpu_list_msm[i].id == + project_info_desc->platform_id) { + snprintf(cpu_type, BUF_SIZE, + "%s", cpu_list_msm[i].name); + break; + } + } + } +} + +static char mainboard_version[16] = {0}; +static char mainboard_manufacture[8] = {'O', + 'N', 'E', 'P', 'L', 'U', 'S', '\0'}; +static char Aboard_version[4] = {0}; +static int Aboard_gpio = 130; + +void init_a_board_gpio(void) +{ + if (gpio_is_valid(Aboard_gpio)) { + if (gpio_request(Aboard_gpio, "ID_ANT_PCBA")) + pr_err("%s: gpio_request(%d) fail!\n", + __func__, Aboard_gpio); + gpio_direction_input(Aboard_gpio); + } else + pr_err("%s: Aboard_gpio %d is invalid\n", + __func__, Aboard_gpio); +} + +uint32 get_hw_version(void) +{ + project_info_desc = smem_find(SMEM_PROJECT_INFO, + sizeof(struct project_info), + 0, + SMEM_ANY_HOST_FLAG); + + if (IS_ERR_OR_NULL(project_info_desc)) + pr_err("%s: get project_info failure\n", __func__); + else { + pr_err("%s: hw version: %d\n", __func__, + project_info_desc->hw_version); + return project_info_desc->hw_version; + } + return 0; +} + + +int __init init_project_info(void) +{ + static bool project_info_init_done; + int ddr_size; + + if (project_info_init_done) + return 0; + + project_info_desc = smem_find(SMEM_PROJECT_INFO, + sizeof(struct project_info), + 0, + SMEM_ANY_HOST_FLAG); + + if (IS_ERR_OR_NULL(project_info_desc)) { + pr_err("%s: get project_info failure\n", __func__); + return 0; + } + pr_err("%s: project_name: %s hw_version: %d rf_v1: %d rf_v2: %d: rf_v3: %d paltform_id:%d\n", + __func__, project_info_desc->project_name, + project_info_desc->hw_version, + project_info_desc->rf_v1, + project_info_desc->rf_v2, + project_info_desc->rf_v3, + project_info_desc->platform_id); + + switch (project_info_desc->hw_version) { + case 11: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVB"); + break; + case 12: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "T0"); + break; + case 13: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "T1"); + break; + case 14: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVT1"); + break; + case 15: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVT2"); + break; + case 21: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVT3"); + break; + case 22: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "DVT1"); + break; + case 23: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "PVT"); + break; + case 24: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "PVT1"); + break; + case 33: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVB"); + break; + case 34: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "T0"); + break; + case 35: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVT1"); + break; + case 41: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "EVT2"); + break; + case 42: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "DVT"); + break; + case 43: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "PVT"); + break; + case 53: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "BACKUP2"); + break; + case 55: + snprintf(mainboard_version, sizeof(mainboard_version), "%s %s", + project_info_desc->project_name, "BACKUP"); + break; + + default: + snprintf(mainboard_version, sizeof(mainboard_version), "%d", + project_info_desc->hw_version); + break; + } + push_component_info(MAINBOARD, + mainboard_version, + mainboard_manufacture); + + init_a_board_gpio(); + snprintf(Aboard_version, sizeof(Aboard_version), "%d", + gpio_get_value(Aboard_gpio)); + push_component_info(ABOARD, Aboard_version, + mainboard_manufacture); + pr_err("%s: Aboard_gpio(%d) value(%d)\n", __func__, Aboard_gpio, + gpio_get_value(Aboard_gpio)); + + get_ddr_manufacture_name(); + + if (totalram_pages > 6*(1<<18)) + ddr_size = 8; + else if (totalram_pages > 5*(1<<18)) + ddr_size = 6; + else if (totalram_pages > 4*(1<<18)) + ddr_size = 5; + else if (totalram_pages > 3*(1<<18)) + ddr_size = 4; + else if (totalram_pages > 2*(1<<18)) + ddr_size = 3; + else if (totalram_pages > 1*(1<<18)) + ddr_size = 2; + + snprintf(ddr_version, sizeof(ddr_version), "size_%dG_r_%d_c_%d", + ddr_size, project_info_desc->ddr_row, + project_info_desc->ddr_column); + snprintf(ddr_manufacture_and_fw_verion, + sizeof(ddr_manufacture_and_fw_verion), + "%s%s %u.%u", ddr_manufacture, + project_info_desc->ddr_reserve_info == 0x05 ? "20nm" : + (project_info_desc->ddr_reserve_info == 0x06 ? "18nm" : " "), + project_info_desc->ddr_fw_version >> 16, + project_info_desc->ddr_fw_version & 0x0000FFFF); + push_component_info(DDR, ddr_version, ddr_manufacture_and_fw_verion); + + get_cpu_type(); + push_component_info(CPU, cpu_type, "Qualcomm"); + project_info_init_done = true; + + return 0; +} + +subsys_initcall(init_project_info); diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c old mode 100644 new mode 100755 index 4ca0edf24eed..63c0c49f6ba2 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2018, 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 @@ -10,8 +10,6 @@ * GNU General Public License for more details. */ -#define pr_fmt(fmt) "haptic: %s: " fmt, __func__ - #include #include #include @@ -27,21 +25,21 @@ #include #include #include -#include -#include #include -#include #include "../../staging/android/timed_output.h" +#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ + IRQF_TRIGGER_FALLING | \ + IRQF_ONESHOT) + #define QPNP_HAP_STATUS(b) (b + 0x0A) #define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) -#define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B) -#define QPNP_HAP_CFG1_REG(b) (b + 0x4C) -#define QPNP_HAP_CFG2_REG(b) (b + 0x4D) -#define QPNP_HAP_SEL_REG(b) (b + 0x4E) +#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) +#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) +#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) #define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F) #define QPNP_HAP_VMAX_REG(b) (b + 0x51) #define QPNP_HAP_ILIM_REG(b) (b + 0x52) @@ -61,49 +59,37 @@ #define QPNP_HAP_TEST2_REG(b) (b + 0xE3) #define QPNP_HAP_STATUS_BUSY 0x02 -#define QPNP_HAP_ACT_TYPE_MASK BIT(0) +#define QPNP_HAP_ACT_TYPE_MASK 0xFE #define QPNP_HAP_LRA 0x0 #define QPNP_HAP_ERM 0x1 -#define QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT BIT(3) -#define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4) +#define QPNP_HAP_AUTO_RES_MODE_MASK 0x8F #define QPNP_HAP_AUTO_RES_MODE_SHIFT 4 -#define QPNP_HAP_PM660_AUTO_RES_MODE_BIT BIT(7) -#define QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT 7 -#define QPNP_HAP_PM660_CALIBRATE_DURATION_MASK GENMASK(6, 5) -#define QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT 5 -#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT BIT(4) -#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT 4 -#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3) -#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3 -#define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0) -#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) -#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 -#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) -#define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0) -#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 -#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 -#define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256 -#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4) -#define QPNP_HAP_WF_SOURCE_SHIFT 4 -#define QPNP_HAP_VMAX_OVD_BIT BIT(6) -#define QPNP_HAP_VMAX_MASK GENMASK(5, 1) +#define QPNP_HAP_LRA_HIGH_Z_MASK 0xF3 +#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 +#define QPNP_HAP_LRA_RES_CAL_PER_MASK 0xFC +#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 +#define QPNP_HAP_PLAY_MODE_MASK 0xCF +#define QPNP_HAP_PLAY_MODE_SHFT 4 +#define QPNP_HAP_VMAX_MASK 0xC1 #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 #define QPNP_HAP_VMAX_MAX_MV 3596 -#define QPNP_HAP_ILIM_MASK BIT(0) +#define QPNP_HAP_ILIM_MASK 0xFE #define QPNP_HAP_ILIM_MIN_MV 400 #define QPNP_HAP_ILIM_MAX_MV 800 -#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0) +#define QPNP_HAP_SC_DEB_MASK 0xF8 +#define QPNP_HAP_SC_DEB_SUB 2 #define QPNP_HAP_SC_DEB_CYCLES_MIN 0 #define QPNP_HAP_DEF_SC_DEB_CYCLES 8 #define QPNP_HAP_SC_DEB_CYCLES_MAX 32 #define QPNP_HAP_SC_CLR 1 -#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0) +#define QPNP_HAP_INT_PWM_MASK 0xFC #define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253 #define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505 #define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739 #define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076 -#define QPNP_HAP_WAV_SHAPE_MASK BIT(0) +#define QPNP_HAP_WAV_SHAPE_MASK 0xFE #define QPNP_HAP_RATE_CFG1_MASK 0xFF #define QPNP_HAP_RATE_CFG2_MASK 0xF0 #define QPNP_HAP_RATE_CFG2_SHFT 8 @@ -111,25 +97,23 @@ #define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0 #define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715 #define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475 -#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4) -#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0) -#define QPNP_HAP_WAV_REP_SHIFT 4 +#define QPNP_HAP_WAV_REP_MASK 0x8F +#define QPNP_HAP_WAV_S_REP_MASK 0xFC +#define QPNP_HAP_WAV_REP_SHFT 4 #define QPNP_HAP_WAV_REP_MIN 1 #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 #define QPNP_HAP_WAV_S_REP_MAX 8 -#define QPNP_HAP_WF_AMP_MASK GENMASK(5, 1) -#define QPNP_HAP_WF_OVD_BIT BIT(6) #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 -#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0) +#define QPNP_HAP_EXT_PWM_MASK 0xFC #define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25 #define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50 #define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75 #define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100 #define PWM_MAX_DTEST_LINES 4 -#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4) +#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F #define QPNP_HAP_EXT_PWM_DTEST_SHFT 4 #define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F #define QPNP_HAP_EXT_PWM_HALF_DUTY 50 @@ -138,41 +122,38 @@ #define QPNP_HAP_WAV_SINE 0 #define QPNP_HAP_WAV_SQUARE 1 #define QPNP_HAP_WAV_SAMP_LEN 8 -#define QPNP_HAP_WAV_SAMP_MAX 0x3E +#define QPNP_HAP_WAV_SAMP_MAX 0x7E #define QPNP_HAP_BRAKE_PAT_LEN 4 -#define QPNP_HAP_PLAY_EN_BIT BIT(7) -#define QPNP_HAP_EN_BIT BIT(7) -#define QPNP_HAP_BRAKE_MASK BIT(0) -#define QPNP_HAP_AUTO_RES_MASK BIT(7) -#define AUTO_RES_ENABLE BIT(7) +#define QPNP_HAP_PLAY_EN 0x80 +#define QPNP_HAP_EN 0x80 +#define QPNP_HAP_BRAKE_MASK 0xFE +#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F +#define QPNP_HAP_SEC_UNLOCK 0xA5 +#define AUTO_RES_ENABLE 0x80 +#define AUTO_RES_DISABLE 0x00 #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 -#define SC_MAX_COUNT 5 +#define SC_MAX_DURATION 5 #define QPNP_HAP_TIMEOUT_MS_MAX 15000 #define QPNP_HAP_STR_SIZE 20 #define QPNP_HAP_MAX_RETRIES 5 +#define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 -#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 -#define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC) +#define AUTO_RES_ENABLE_TIMEOUT 20000 +#define AUTO_RES_ERR_CAPTURE_RES 5 +#define AUTO_RES_ERR_MAX 15 -#define MAX_POSITIVE_VARIATION_LRA_FREQ 30 -#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30 -#define FREQ_VARIATION_STEP 5 -#define AUTO_RES_ERROR_CAPTURE_RES 5 -#define AUTO_RES_ERROR_MAX 30 -#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \ - ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \ - / FREQ_VARIATION_STEP) -#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 + rc_clk_err_percent_x10)) / 1000) -#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \ - (hap->init_drive_period_code = (hap->init_drive_period_code * \ - (1000 - rc_clk_err_percent_x10)) / 1000) +#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 +#define MISC_SEC_ACCESS 0x09D0 +#define MISC_SEC_UNLOCK 0xA5 +#define PMI8950_MISC_SID 2 -u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; +#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC) + +#define LRA_POS_FREQ_COUNT 6 +int lra_play_rate_code[LRA_POS_FREQ_COUNT]; /* haptic debug register set */ static u8 qpnp_hap_dbg_regs[] = { @@ -229,14 +210,9 @@ enum qpnp_hap_auto_res_mode { QPNP_HAP_AUTO_RES_ZXD_EOP, }; -enum qpnp_hap_pm660_auto_res_mode { - QPNP_HAP_PM660_AUTO_RES_ZXD, - QPNP_HAP_PM660_AUTO_RES_QWD, -}; - /* high Z option lines */ enum qpnp_hap_high_z { - QPNP_HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */ + QPNP_HAP_LRA_HIGH_Z_NONE, QPNP_HAP_LRA_HIGH_Z_OPT1, QPNP_HAP_LRA_HIGH_Z_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT3, @@ -250,11 +226,6 @@ enum qpnp_hap_mode { QPNP_HAP_PWM, }; -/* status flags */ -enum qpnp_hap_status { - AUTO_RESONANCE_ENABLED = BIT(0), -}; - /* pwm channel info */ struct qpnp_pwm_info { struct pwm_device *pwm_dev; @@ -263,22 +234,6 @@ struct qpnp_pwm_info { u32 period_us; }; -/* - * qpnp_hap_lra_ares_cfg - Haptic auto_resonance configuration - * @ lra_qwd_drive_duration - LRA QWD drive duration - * @ calibrate_at_eop - Calibrate at EOP - * @ lra_res_cal_period - LRA resonance calibration period - * @ auto_res_mode - auto resonace mode - * @ lra_high_z - high z option line - */ -struct qpnp_hap_lra_ares_cfg { - int lra_qwd_drive_duration; - int calibrate_at_eop; - enum qpnp_hap_high_z lra_high_z; - u16 lra_res_cal_period; - u8 auto_res_mode; -}; - /* * qpnp_hap - Haptic data structure * @ spmi - spmi device @@ -286,58 +241,45 @@ struct qpnp_hap_lra_ares_cfg { * @ auto_res_err_poll_timer - hrtimer for auto-resonance error * @ timed_dev - timed output device * @ work - worker + * @ auto_res_err_work - correct auto resonance error * @ sc_work - worker to handle short circuit condition * @ pwm_info - pwm info - * @ ares_cfg - auto resonance configuration * @ lock - mutex lock * @ wf_lock - mutex lock for waveform - * @ init_drive_period_code - the initial lra drive period code - * @ drive_period_code_max_limit_percent_variation - maximum limit of - percentage variation of drive period code - * @ drive_period_code_min_limit_percent_variation - minimum limit og - percentage variation of drive period code - * @ drive_period_code_max_limit - calculated drive period code with - percentage variation on the higher side. - * @ drive_period_code_min_limit - calculated drive period code with - percentage variation on the lower side * @ play_mode - play mode + * @ auto_res_mode - auto resonace mode + * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms - * @ time_required_to_generate_back_emf_us - the time required for sufficient - back-emf to be generated for auto resonance to be successful * @ vmax_mv - max voltage in mv * @ ilim_ma - limiting current in ma * @ sc_deb_cycles - short circuit debounce cycles * @ int_pwm_freq_khz - internal pwm frequency in khz * @ wave_play_rate_us - play rate for waveform - * @ play_time_ms - play time set by the user * @ ext_pwm_freq_khz - external pwm frequency in khz * @ wave_rep_cnt - waveform repeat count * @ wave_s_rep_cnt - waveform sample repeat count * @ play_irq - irq for play * @ sc_irq - irq for short circuit - * @ status_flags - status * @ base - base address * @ act_type - actuator type * @ wave_shape - waveform shape * @ wave_samp - array of wave samples * @ shadow_wave_samp - shadow array of wave samples * @ brake_pat - pattern for active breaking - * @ sc_count - counter to determine the duration of short circuit condition - * @ lra_hw_auto_resonance - enable hardware auto resonance + * @ reg_en_ctl - enable control register + * @ reg_play - play register + * @ lra_res_cal_period - period for resonance calibration + * @ sc_duration - counter to determine the duration of short circuit condition * @ state - current state of haptics - * @ module_en - haptics module enable status + * @ use_play_irq - play irq usage state + * @ use_sc_irq - short circuit irq usage state * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state + * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency - * @ misc_clk_trim_error_reg - MISC clock trim error register if present - * @ clk_trim_error_code - MISC clock trim error code - * @ perform_lra_auto_resonance_search - whether lra auto resonance search - * algorithm should be performed or not. - * @ auto_mode - Auto mode selection - * @ override_auto_mode_config - Flag to override auto mode configuration with - * user specified values through sysfs. + * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present */ struct qpnp_hap { struct platform_device *pdev; @@ -347,245 +289,183 @@ struct qpnp_hap { struct hrtimer auto_res_err_poll_timer; struct timed_output_dev timed_dev; struct work_struct work; + struct work_struct auto_res_err_work; struct delayed_work sc_work; struct hrtimer hap_test_timer; struct work_struct test_work; struct qpnp_pwm_info pwm_info; - struct qpnp_hap_lra_ares_cfg ares_cfg; struct mutex lock; struct mutex wf_lock; - spinlock_t bus_lock; struct completion completion; enum qpnp_hap_mode play_mode; - u32 misc_clk_trim_error_reg; - u32 init_drive_period_code; + enum qpnp_hap_auto_res_mode auto_res_mode; + enum qpnp_hap_high_z lra_high_z; u32 timeout_ms; - u32 time_required_to_generate_back_emf_us; u32 vmax_mv; u32 ilim_ma; u32 sc_deb_cycles; u32 int_pwm_freq_khz; u32 wave_play_rate_us; - u32 play_time_ms; u32 ext_pwm_freq_khz; u32 wave_rep_cnt; u32 wave_s_rep_cnt; u32 play_irq; u32 sc_irq; - u32 status_flags; u16 base; - u16 last_rate_cfg; - u16 drive_period_code_max_limit; - u16 drive_period_code_min_limit; - u8 drive_period_code_max_limit_percent_variation; - u8 drive_period_code_min_limit_percent_variation; u8 act_type; u8 wave_shape; - u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; - u8 sc_count; + u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 wave_samp_overdrive[QPNP_HAP_WAV_SAMP_LEN]; + u8 wave_samp_normal[QPNP_HAP_WAV_SAMP_LEN]; + u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; + u8 reg_en_ctl; + u8 reg_play; + u8 lra_res_cal_period; + u8 sc_duration; u8 ext_pwm_dtest_line; - u8 pmic_subtype; - u8 clk_trim_error_code; - bool lra_hw_auto_resonance; - bool vcc_pon_enabled; bool state; - bool module_en; + bool use_play_irq; + bool use_sc_irq; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; + bool buffer_cfg_state; bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; - bool perform_lra_auto_resonance_search; - bool auto_mode; - bool override_auto_mode_config; - bool play_irq_en; + bool misc_trim_error_rc19p2_clk_reg_present; + int resonant_frequency; + int enable_time; }; static struct qpnp_hap *ghap; +static struct workqueue_struct *vibqueue; /* helper to read a pmic register */ -static int qpnp_hap_read_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val, - int len) +static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr) { int rc; + uint val; - rc = regmap_bulk_read(hap->regmap, addr, val, len); + rc = regmap_read(hap->regmap, addr, &val); if (rc < 0) - pr_err("Error reading address: %X - ret %X\n", addr, rc); - - return rc; -} - -static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) -{ - int rc; - uint tmp; - - rc = regmap_read(hap->regmap, addr, &tmp); - if (rc < 0) - pr_err("Error reading address: %X - ret %X\n", addr, rc); - else - *val = (u8)tmp; - + dev_err(&hap->pdev->dev, + "Error reading address: %X - ret %X\n", addr, rc); + *data = (u8)val; return rc; } /* helper to write a pmic register */ -static int qpnp_hap_write_mult_reg(struct qpnp_hap *hap, u16 addr, u8 *val, - int len) +static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) { - unsigned long flags; int rc; - spin_lock_irqsave(&hap->bus_lock, flags); - rc = regmap_bulk_write(hap->regmap, addr, val, len); + rc = regmap_write(hap->regmap, addr, *data); if (rc < 0) - pr_err("Error writing address: %X - ret %X\n", addr, rc); + dev_err(&hap->pdev->dev, + "Error writing address: %X - ret %X\n", addr, rc); - spin_unlock_irqrestore(&hap->bus_lock, flags); - return rc; -} - -static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&hap->bus_lock, flags); - rc = regmap_write(hap->regmap, addr, val); - if (rc < 0) - pr_err("Error writing address: %X - ret %X\n", addr, rc); - - spin_unlock_irqrestore(&hap->bus_lock, flags); - if (!rc) - pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); + dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data); return rc; } /* helper to access secure registers */ -#define QPNP_HAP_SEC_UNLOCK 0xA5 -static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr, - u8 mask, u8 val) +static int qpnp_hap_sec_access(struct qpnp_hap *hap) { - unsigned long flags; int rc; - u8 tmp = QPNP_HAP_SEC_UNLOCK; + u8 reg = QPNP_HAP_SEC_UNLOCK; - spin_lock_irqsave(&hap->bus_lock, flags); - rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp); - if (rc < 0) { - pr_err("Error writing sec_code - ret %X\n", rc); - goto out; - } + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_SEC_ACCESS_REG(hap->base)); + if (rc) + return rc; - rc = regmap_update_bits(hap->regmap, addr, mask, val); - if (rc < 0) - pr_err("Error writing address: %X - ret %X\n", addr, rc); - -out: - spin_unlock_irqrestore(&hap->bus_lock, flags); - if (!rc) - pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); - return rc; -} - -static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask, - u8 val) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(&hap->bus_lock, flags); - rc = regmap_update_bits(hap->regmap, addr, mask, val); - if (rc < 0) - pr_err("Error writing address: %X - ret %X\n", addr, rc); - - spin_unlock_irqrestore(&hap->bus_lock, flags); - if (!rc) - pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); - return rc; + return 0; } static void qpnp_handle_sc_irq(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, sc_work.work); - u8 val; + u8 val, reg; - qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); + qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); /* clear short circuit register */ if (val & SC_FOUND_BIT) { - hap->sc_count++; - val = QPNP_HAP_SC_CLR; - qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val); + hap->sc_duration++; + reg = QPNP_HAP_SC_CLR; + qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_CLR_REG(hap->base)); } } -#define QPNP_HAP_CYCLES 4 -static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on) +static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) { - unsigned long wait_time_us; u8 val; + u8 data; int rc, i; - if (hap->module_en == on) - return 0; - - if (!on) { - /* - * Wait for 4 cycles of play rate for play time is > 20 ms and - * wait for play time when play time is < 20 ms. This way, there - * will be an improvement in waiting time polling BUSY status. - */ - if (hap->play_time_ms <= 20) - wait_time_us = hap->play_time_ms * 1000; - else - wait_time_us = QPNP_HAP_CYCLES * hap->wave_play_rate_us; - + val = hap->reg_en_ctl; + if (on) { + val |= QPNP_HAP_EN; + } else { for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { - rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), - &val); - if (rc < 0) - return rc; + /* wait for 4 cycles of play rate */ + unsigned long sleep_time = + QPNP_HAP_CYCLS * hap->wave_play_rate_us; - pr_debug("HAP_STATUS=0x%x\n", val); + rc = qpnp_hap_read_reg(hap, &data, + QPNP_HAP_STATUS(hap->base)); - /* wait for play_rate cycles */ - if (val & QPNP_HAP_STATUS_BUSY) { - usleep_range(wait_time_us, wait_time_us + 1); + dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", data); + + /* wait for QPNP_HAP_CYCLS cycles of play rate */ + if (data & QPNP_HAP_STATUS_BUSY) { + usleep_range(sleep_time, sleep_time + 1); if (hap->play_mode == QPNP_HAP_DIRECT || hap->play_mode == QPNP_HAP_PWM) break; - } else { + } else break; - } } if (i >= QPNP_HAP_MAX_RETRIES) - pr_debug("Haptics Busy. Force disable\n"); + dev_dbg(&hap->pdev->dev, + "Haptics Busy. Force disable\n"); + + val &= ~QPNP_HAP_EN; } - val = on ? QPNP_HAP_EN_BIT : 0; - rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); + rc = qpnp_hap_write_reg(hap, &val, + QPNP_HAP_EN_CTL_REG(hap->base)); if (rc < 0) return rc; - hap->module_en = on; + hap->reg_en_ctl = val; + return 0; } -static int qpnp_hap_play(struct qpnp_hap *hap, bool on) +static int qpnp_hap_play(struct qpnp_hap *hap, int on) { u8 val; int rc; - val = on ? QPNP_HAP_PLAY_EN_BIT : 0; - rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val); - return rc; + val = hap->reg_play; + if (on) + val |= QPNP_HAP_PLAY_EN; + else + val &= ~QPNP_HAP_PLAY_EN; + + rc = qpnp_hap_write_reg(hap, &val, + QPNP_HAP_PLAY_REG(hap->base)); + if (rc < 0) + return rc; + + hap->reg_play = val; + + return 0; } /* sysfs show debug registers */ @@ -599,7 +479,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { - qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val); + qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], @@ -617,15 +497,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int i, rc; - u8 val; + u8 reg; mutex_lock(&hap->wf_lock); /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) { - val = hap->wave_samp[i] = hap->shadow_wave_samp[i]; - rc = qpnp_hap_write_reg(hap, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); + reg = hap->wave_samp[i] = hap->shadow_wave_samp[i]; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); if (rc) goto unlock; } @@ -642,37 +522,36 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int rc; + u8 disable_haptics = 0x00; u8 val; - pr_debug("Short circuit detected\n"); + dev_dbg(&hap->pdev->dev, "Short circuit detected\n"); - if (hap->sc_count < SC_MAX_COUNT) { - qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); + if (hap->sc_duration < SC_MAX_DURATION) { + qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); else - hap->sc_count = 0; + hap->sc_duration = 0; } else { /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - val = 0; - rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), - val); - pr_err("Haptics disabled permanently due to short circuit\n"); + rc = qpnp_hap_write_reg(hap, &disable_haptics, + QPNP_HAP_EN_CTL_REG(hap->base)); + dev_err(&hap->pdev->dev, + "Haptics disabled permanently due to short circuit\n"); } return IRQ_HANDLED; } /* configuration api for buffer mode */ -static int qpnp_hap_buffer_config(struct qpnp_hap *hap, u8 *wave_samp, - bool overdrive) +static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { - u8 buf[QPNP_HAP_WAV_SAMP_LEN], val; - u8 *ptr; - int rc, i; + u8 reg = 0; + int rc, i, temp; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) @@ -685,75 +564,104 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap, u8 *wave_samp, else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; - val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT | - ilog2(hap->wave_s_rep_cnt); - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), - QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val); + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_WAV_REP_MASK; + temp = fls(hap->wave_rep_cnt) - 1; + reg |= (temp << QPNP_HAP_WAV_REP_SHFT); + reg &= QPNP_HAP_WAV_S_REP_MASK; + temp = fls(hap->wave_s_rep_cnt) - 1; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; - /* Don't set override bit in waveform sample for PM660 */ - if (hap->pmic_subtype == PM660_SUBTYPE) - overdrive = false; - - if (wave_samp) - ptr = wave_samp; - else - ptr = hap->wave_samp; - /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ - for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { - buf[i] = ptr[i] & QPNP_HAP_WF_AMP_MASK; - if (buf[i]) - buf[i] |= (overdrive ? QPNP_HAP_WF_OVD_BIT : 0); + for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { + reg = hap->wave_samp[i]; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + if (rc) + return rc; } - rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_WAV_S_REG_BASE(hap->base), - buf, QPNP_HAP_WAV_SAMP_LEN); - if (rc) - return rc; + /* setup play irq */ + if (hap->use_play_irq) { + rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, + NULL, qpnp_hap_play_irq, + QPNP_IRQ_FLAGS, + "qpnp_play_irq", hap); + if (rc < 0) { + dev_err(&hap->pdev->dev, + "Unable to request play(%d) IRQ(err:%d)\n", + hap->play_irq, rc); + return rc; + } + } + hap->buffer_cfg_state = true; return 0; } /* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { - u8 val = 0; - int rc; + u8 reg = 0; + int rc, temp; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; - val = 0; + temp = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; - val = 1; + temp = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; - val = 2; + temp = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; - val = 3; + temp = 3; } - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base), - QPNP_HAP_EXT_PWM_MASK, val); + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_EXT_PWM_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_EXT_PWM_MASK; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_EXT_PWM_REG(hap->base)); if (rc) return rc; + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_TEST2_REG(hap->base)); + if (rc) + return rc; if (!hap->ext_pwm_dtest_line || hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { - pr_err("invalid dtest line\n"); + dev_err(&hap->pdev->dev, "invalid dtest line\n"); return -EINVAL; } /* disable auto res for PWM mode */ - val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; - rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base), - QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val); + reg &= QPNP_HAP_EXT_PWM_DTEST_MASK; + temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; + reg |= temp; + + /* TEST2 is a secure access register */ + rc = qpnp_hap_sec_access(hap); + if (rc) + return rc; + + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_TEST2_REG(hap->base)); if (rc) return rc; @@ -761,7 +669,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc < 0) { - pr_err("hap pwm config failed\n"); + dev_err(&hap->pdev->dev, "hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } @@ -771,240 +679,73 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) return 0; } -static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap, - struct qpnp_hap_lra_ares_cfg *tmp_cfg) -{ - struct qpnp_hap_lra_ares_cfg *ares_cfg; - int rc; - u8 val = 0, mask = 0; - - /* disable auto resonance for ERM */ - if (hap->act_type == QPNP_HAP_ERM) { - val = 0x00; - rc = qpnp_hap_write_reg(hap, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val); - return rc; - } - - if (hap->lra_hw_auto_resonance) { - rc = qpnp_hap_masked_write_reg(hap, - QPNP_HAP_AUTO_RES_CTRL(hap->base), - QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT, - QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT); - if (rc) - return rc; - } - - if (tmp_cfg) - ares_cfg = tmp_cfg; - else - ares_cfg = &hap->ares_cfg; - - if (ares_cfg->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) - ares_cfg->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; - - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (ares_cfg->lra_res_cal_period > - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) - ares_cfg->lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; - - if (ares_cfg->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) - ares_cfg->lra_res_cal_period = 0; - - if (ares_cfg->lra_res_cal_period) - val = ilog2(ares_cfg->lra_res_cal_period / - QPNP_HAP_RES_CAL_PERIOD_MIN) + 1; - } else { - if (ares_cfg->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) - ares_cfg->lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MAX; - - if (ares_cfg->lra_res_cal_period) - val = ilog2(ares_cfg->lra_res_cal_period / - QPNP_HAP_RES_CAL_PERIOD_MIN); - } - - if (hap->pmic_subtype == PM660_SUBTYPE) { - val |= ares_cfg->auto_res_mode << - QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; - mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; - val |= ares_cfg->lra_high_z << - QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; - if (ares_cfg->lra_qwd_drive_duration != -EINVAL) { - val |= ares_cfg->lra_qwd_drive_duration << - QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; - mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; - } - if (ares_cfg->calibrate_at_eop != -EINVAL) { - val |= ares_cfg->calibrate_at_eop << - QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; - mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; - } - mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; - } else { - val |= (ares_cfg->auto_res_mode << - QPNP_HAP_AUTO_RES_MODE_SHIFT); - val |= (ares_cfg->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); - mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK | - QPNP_HAP_LRA_RES_CAL_PER_MASK; - } - - pr_debug("mode: %d hi_z period: %d cal_period: %d\n", - ares_cfg->auto_res_mode, ares_cfg->lra_high_z, - ares_cfg->lra_res_cal_period); - - rc = qpnp_hap_masked_write_reg(hap, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val); - return rc; -} - /* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { - u8 val = 0; - int rc; + u8 reg = 0; + int rc, temp; - val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT; - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base), - QPNP_HAP_WF_SOURCE_MASK, val); - return rc; + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_PLAY_MODE_MASK; + temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); + if (rc) + return rc; + return 0; } -/* configuration api for max voltage */ -static int qpnp_hap_vmax_config(struct qpnp_hap *hap, int vmax_mv, - bool overdrive) +/* configuration api for max volatge */ +static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { - u8 val = 0; - int rc; + u8 reg = 0; + int rc, temp; - if (vmax_mv < 0) - return -EINVAL; + if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) + hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; + else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) + hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; - /* Allow setting override bit in VMAX_CFG only for PM660 */ - if (hap->pmic_subtype != PM660_SUBTYPE) - overdrive = false; + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_VMAX_MASK; + temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; + reg |= (temp << QPNP_HAP_VMAX_SHIFT); - if (vmax_mv < QPNP_HAP_VMAX_MIN_MV) - vmax_mv = QPNP_HAP_VMAX_MIN_MV; - else if (vmax_mv > QPNP_HAP_VMAX_MAX_MV) - vmax_mv = QPNP_HAP_VMAX_MAX_MV; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); + if (rc) + return rc; - val = (vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; - if (overdrive) - val |= QPNP_HAP_VMAX_OVD_BIT; - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base), - QPNP_HAP_VMAX_MASK | QPNP_HAP_VMAX_OVD_BIT, val); - return rc; -} - -/* configuration api for ilim */ -static int qpnp_hap_ilim_config(struct qpnp_hap *hap) -{ - u8 val = 0; - int rc; - - if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; - else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; - - val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1; - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base), - QPNP_HAP_ILIM_MASK, val); - return rc; + return 0; } /* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { - u8 val = 0; - int rc; + u8 reg = 0; + int rc, temp; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; - if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN) - val = ilog2(hap->sc_deb_cycles / - QPNP_HAP_DEF_SC_DEB_CYCLES) + 1; - else - val = QPNP_HAP_SC_DEB_CYCLES_MIN; - - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base), - QPNP_HAP_SC_DEB_MASK, val); - - return rc; -} - -static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap) -{ - int rc; - u8 val; - - if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - val = 1; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; - val = 0; - } - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - val = 1; - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; - val = 2; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; - val = 3; + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_SC_DEB_MASK; + if (hap->sc_deb_cycles) { + temp = fls(hap->sc_deb_cycles) - 1; + reg |= temp - QPNP_HAP_SC_DEB_SUB; } - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base), - QPNP_HAP_INT_PWM_MASK, val); + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); if (rc) return rc; - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base), - QPNP_HAP_INT_PWM_MASK, val); - return rc; -} - -static int qpnp_hap_brake_config(struct qpnp_hap *hap, u8 *brake_pat) -{ - int rc, i; - u32 temp; - u8 *pat_ptr, val; - - if (!hap->en_brake) - return 0; - - /* Configure BRAKE register */ - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), - QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); - if (rc) - return rc; - - if (!brake_pat) - pat_ptr = hap->brake_pat; - else - pat_ptr = brake_pat; - - if (hap->sup_brake_pat) { - for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { - pat_ptr[i] &= QPNP_HAP_BRAKE_PAT_MASK; - temp = i << 1; - val |= pat_ptr[i] << temp; - } - rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), - val); - if (rc) - return rc; - } - return 0; } @@ -1016,16 +757,13 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) u32 temp; int rc, i; - if (hap->wave_rep_cnt > 0 || hap->wave_s_rep_cnt > 0) - return 0; - hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN; rc = of_property_read_u32(pdev->dev.of_node, "qcom,wave-rep-cnt", &temp); if (!rc) { hap->wave_rep_cnt = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read rep cnt\n"); + dev_err(&pdev->dev, "Unable to read rep cnt\n"); return rc; } @@ -1035,18 +773,40 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_s_rep_cnt = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read samp rep cnt\n"); + dev_err(&pdev->dev, "Unable to read samp rep cnt\n"); return rc; } prop = of_find_property(pdev->dev.of_node, "qcom,wave-samples", &temp); if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { - pr_err("Invalid wave samples, use default"); + dev_err(&pdev->dev, "Invalid wave samples, use default"); for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX; } else { memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN); + memcpy(hap->wave_samp_normal, prop->value, + QPNP_HAP_WAV_SAMP_LEN); + } + prop = of_find_property(pdev->dev.of_node, + "qcom,wave-samples-overdrive", &temp); + if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { + dev_err(&pdev->dev, "Invalid wave samples, use default"); + for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) + hap->wave_samp_overdrive[i] = QPNP_HAP_WAV_SAMP_MAX; + } else { + memcpy(hap->wave_samp_overdrive, prop->value, + QPNP_HAP_WAV_SAMP_LEN); + } + + hap->use_play_irq = of_property_read_bool(pdev->dev.of_node, + "qcom,use-play-irq"); + if (hap->use_play_irq) { + hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); + if (hap->play_irq < 0) { + dev_err(&pdev->dev, "Unable to get play irq\n"); + return hap->play_irq; + } } return 0; @@ -1065,7 +825,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (!rc) { hap->ext_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read ext pwm freq\n"); + dev_err(&pdev->dev, "Unable to read ext pwm freq\n"); return rc; } @@ -1080,7 +840,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (IS_ERR(hap->pwm_info.pwm_dev)) { rc = PTR_ERR(hap->pwm_info.pwm_dev); - pr_err("Cannot get PWM device rc:(%d)\n", rc); + dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc); hap->pwm_info.pwm_dev = NULL; return rc; } @@ -1115,7 +875,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index) timed_dev); if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - pr_err("Invalid sample index(%d)\n", index); + dev_err(dev, "Invalid sample index(%d)\n", index); return -EINVAL; } @@ -1181,7 +941,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, int data, rc; if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - pr_err("Invalid sample index(%d)\n", index); + dev_err(dev, "Invalid sample index(%d)\n", index); return -EINVAL; } @@ -1190,7 +950,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, return rc; if (data < 0 || data > 0xff) { - pr_err("Invalid sample wf_%d (%d)\n", index, data); + dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data); return -EINVAL; } @@ -1198,6 +958,20 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, return count; } +static int qpnp_hap_wf_samp_store_all(struct timed_output_dev *timed_dev, + bool use_overdrive) +{ + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + memcpy(hap->shadow_wave_samp, use_overdrive ? hap->wave_samp_overdrive : + hap->wave_samp_normal, QPNP_HAP_WAV_SAMP_LEN); + mutex_lock(&hap->wf_lock); + hap->wf_update = true; + mutex_unlock(&hap->wf_lock); + + return 0; +} + static ssize_t qpnp_hap_wf_s0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -1246,6 +1020,83 @@ static ssize_t qpnp_hap_wf_s7_store(struct device *dev, return qpnp_hap_wf_samp_store(dev, buf, count, 7); } +static ssize_t qpnp_hap_rf_hz_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; + int temp; + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + + qpnp_hap_read_reg(hap, &lra_auto_res_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + qpnp_hap_read_reg(hap, &lra_auto_res_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + + temp = (lra_auto_res_hi << 8) | (lra_auto_res_lo & 0xff); + + hap->resonant_frequency = ((19200/96)*1000)/temp; + + return snprintf(buf, PAGE_SIZE, "%d\n", hap->resonant_frequency); +} + +static ssize_t qpnp_hap_rf_hz_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} +static ssize_t qpnp_hap_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg = 0; + int rc; + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); + reg = (reg >> QPNP_HAP_VMAX_SHIFT); + + hap->vmax_mv = reg * QPNP_HAP_VMAX_MIN_MV; + + return snprintf(buf, PAGE_SIZE, "%d\n", hap->vmax_mv); +} +static ssize_t qpnp_hap_vmax_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int data, rc, temp; + u8 reg; + struct timed_output_dev *timed_dev = dev_get_drvdata(dev); + struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, + timed_dev); + + rc = kstrtoint(buf, 10, &data); + if (rc) + return rc; + + if (data < QPNP_HAP_VMAX_MIN_MV) + data = QPNP_HAP_VMAX_MIN_MV; + else if (data > QPNP_HAP_VMAX_MAX_MV) + data = QPNP_HAP_VMAX_MAX_MV; + + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); + if (rc < 0) + return rc; + + reg &= QPNP_HAP_VMAX_MASK; + temp = data / QPNP_HAP_VMAX_MIN_MV; + reg |= (temp << QPNP_HAP_VMAX_SHIFT); + + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); + + if (rc) + return rc; + + hap->vmax_mv = data; + + return count; +} + /* sysfs show for wave form update */ static ssize_t qpnp_hap_wf_update_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1290,8 +1141,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc; - u8 val; + int data, rc, temp; + u8 reg; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1302,11 +1153,19 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_REP_MAX) data = QPNP_HAP_WAV_REP_MAX; - val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT; - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), - QPNP_HAP_WAV_REP_MASK, val); - if (!rc) - hap->wave_rep_cnt = data; + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_WAV_REP_MASK; + temp = fls(data) - 1; + reg |= (temp << QPNP_HAP_WAV_REP_SHFT); + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); + if (rc) + return rc; + + hap->wave_rep_cnt = data; return count; } @@ -1329,8 +1188,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc; - u8 val; + int data, rc, temp; + u8 reg; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1341,34 +1200,23 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; - val = ilog2(hap->wave_s_rep_cnt); - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), - QPNP_HAP_WAV_S_REP_MASK, val); - if (!rc) - hap->wave_s_rep_cnt = data; + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_WAV_S_REP_MASK; + temp = fls(data) - 1; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_REP_REG(hap->base)); + if (rc) + return rc; + + hap->wave_s_rep_cnt = data; return count; } -static int parse_string(const char *in_buf, char *out_buf) -{ - int i; - - if (snprintf(out_buf, QPNP_HAP_STR_SIZE, "%s", in_buf) - > QPNP_HAP_STR_SIZE) - return -EINVAL; - - for (i = 0; i < strlen(out_buf); i++) { - if (out_buf[i] == ' ' || out_buf[i] == '\n' || - out_buf[i] == '\t') { - out_buf[i] = '\0'; - break; - } - } - - return 0; -} - /* sysfs store function for play mode*/ static ssize_t qpnp_hap_play_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1377,12 +1225,17 @@ static ssize_t qpnp_hap_play_mode_store(struct device *dev, struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); char str[QPNP_HAP_STR_SIZE + 1]; - int rc = 0, temp, old_mode; + int rc = 0, temp, old_mode, i; - rc = parse_string(buf, str); - if (rc < 0) - return rc; + if (snprintf(str, QPNP_HAP_STR_SIZE, "%s", buf) > QPNP_HAP_STR_SIZE) + return -EINVAL; + for (i = 0; i < strlen(str); i++) { + if (str[i] == ' ' || str[i] == '\n' || str[i] == '\t') { + str[i] = '\0'; + break; + } + } if (strcmp(str, "buffer") == 0) temp = QPNP_HAP_BUFFER; else if (strcmp(str, "direct") == 0) @@ -1397,10 +1250,10 @@ static ssize_t qpnp_hap_play_mode_store(struct device *dev, if (temp == hap->play_mode) return count; - if (temp == QPNP_HAP_BUFFER) { + if (temp == QPNP_HAP_BUFFER && !hap->buffer_cfg_state) { rc = qpnp_hap_parse_buffer_dt(hap); if (!rc) - rc = qpnp_hap_buffer_config(hap, NULL, false); + rc = qpnp_hap_buffer_config(hap); } else if (temp == QPNP_HAP_PWM && !hap->pwm_cfg_state) { rc = qpnp_hap_parse_pwm_dt(hap); if (!rc) @@ -1549,247 +1402,14 @@ static ssize_t qpnp_hap_ramp_test_data_show(struct device *dev, } -static ssize_t qpnp_hap_auto_res_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - char *str; - - if (hap->pmic_subtype == PM660_SUBTYPE) { - switch (hap->ares_cfg.auto_res_mode) { - case QPNP_HAP_PM660_AUTO_RES_ZXD: - str = "ZXD"; - break; - case QPNP_HAP_PM660_AUTO_RES_QWD: - str = "QWD"; - break; - default: - str = "None"; - break; - } - } else { - switch (hap->ares_cfg.auto_res_mode) { - case QPNP_HAP_AUTO_RES_NONE: - str = "None"; - break; - case QPNP_HAP_AUTO_RES_ZXD: - str = "ZXD"; - break; - case QPNP_HAP_AUTO_RES_QWD: - str = "QWD"; - break; - case QPNP_HAP_AUTO_RES_MAX_QWD: - str = "MAX_QWD"; - break; - case QPNP_HAP_AUTO_RES_ZXD_EOP: - str = "ZXD_EOP"; - break; - default: - str = "None"; - break; - } - } - - return snprintf(buf, PAGE_SIZE, "%s\n", str); -} - -static ssize_t qpnp_hap_auto_res_mode_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - char str[QPNP_HAP_STR_SIZE + 1]; - int rc = 0, temp; - - rc = parse_string(buf, str); - if (rc < 0) - return rc; - - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (strcmp(str, "ZXD") == 0 || - strcmp(str, "zxd") == 0) - temp = QPNP_HAP_PM660_AUTO_RES_ZXD; - else if (strcmp(str, "QWD") == 0 || - strcmp(str, "qwd") == 0) - temp = QPNP_HAP_PM660_AUTO_RES_QWD; - else { - pr_err("Should be ZXD or QWD\n"); - return -EINVAL; - } - } else { - if (strcmp(str, "None") == 0) - temp = QPNP_HAP_AUTO_RES_NONE; - else if (strcmp(str, "ZXD") == 0 || - strcmp(str, "zxd") == 0) - temp = QPNP_HAP_AUTO_RES_ZXD; - else if (strcmp(str, "QWD") == 0 || - strcmp(str, "qwd") == 0) - temp = QPNP_HAP_AUTO_RES_QWD; - else if (strcmp(str, "ZXD_EOP") == 0 || - strcmp(str, "zxd_eop") == 0) - temp = QPNP_HAP_AUTO_RES_ZXD_EOP; - else if (strcmp(str, "MAX_QWD") == 0 || - strcmp(str, "max_qwd") == 0) - temp = QPNP_HAP_AUTO_RES_MAX_QWD; - else { - pr_err("Should be None or ZXD or QWD or ZXD_EOP or MAX_QWD\n"); - return -EINVAL; - } - } - - hap->ares_cfg.auto_res_mode = temp; - return count; -} - -static ssize_t qpnp_hap_hi_z_period_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - char *str; - - switch (hap->ares_cfg.lra_high_z) { - case QPNP_HAP_LRA_HIGH_Z_NONE: - str = "high_z_none"; - break; - case QPNP_HAP_LRA_HIGH_Z_OPT1: - str = "high_z_opt1"; - break; - case QPNP_HAP_LRA_HIGH_Z_OPT2: - str = "high_z_opt2"; - break; - case QPNP_HAP_LRA_HIGH_Z_OPT3: - str = "high_z_opt3"; - break; - } - - return snprintf(buf, PAGE_SIZE, "%s\n", str); -} - -static ssize_t qpnp_hap_hi_z_period_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - int data, rc; - - rc = kstrtoint(buf, 10, &data); - if (rc) - return rc; - - if (data < QPNP_HAP_LRA_HIGH_Z_NONE - || data > QPNP_HAP_LRA_HIGH_Z_OPT3) { - pr_err("Invalid high Z configuration\n"); - return -EINVAL; - } - - hap->ares_cfg.lra_high_z = data; - return count; -} - -static ssize_t qpnp_hap_calib_period_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", - hap->ares_cfg.lra_res_cal_period); -} - -static ssize_t qpnp_hap_calib_period_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - int data, rc; - - rc = kstrtoint(buf, 10, &data); - if (rc) - return rc; - - if (data < QPNP_HAP_RES_CAL_PERIOD_MIN) { - pr_err("Invalid auto resonance calibration period\n"); - return -EINVAL; - } - - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (data > QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) { - pr_err("Invalid auto resonance calibration period\n"); - return -EINVAL; - } - } else { - if (data > QPNP_HAP_RES_CAL_PERIOD_MAX) { - pr_err("Invalid auto resonance calibration period\n"); - return -EINVAL; - } - } - - hap->ares_cfg.lra_res_cal_period = data; - return count; -} - -static ssize_t qpnp_hap_override_auto_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", hap->override_auto_mode_config); -} - -static ssize_t qpnp_hap_override_auto_mode_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - int data, rc; - - rc = kstrtoint(buf, 10, &data); - if (rc) - return rc; - - hap->override_auto_mode_config = data; - return count; -} - -static ssize_t qpnp_hap_vmax_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - - return snprintf(buf, PAGE_SIZE, "%d\n", hap->vmax_mv); -} - -static ssize_t qpnp_hap_vmax_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct timed_output_dev *timed_dev = dev_get_drvdata(dev); - struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, - timed_dev); - int data, rc; - - rc = kstrtoint(buf, 10, &data); - if (rc) - return rc; - - hap->vmax_mv = data; - return count; -} - /* sysfs attributes */ static struct device_attribute qpnp_hap_attrs[] = { + __ATTR(rf_hz, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_hap_rf_hz_show, + qpnp_hap_rf_hz_store), + __ATTR(vmax_mv, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_hap_vmax_show, + qpnp_hap_vmax_store), __ATTR(wf_s0, 0664, qpnp_hap_wf_s0_show, qpnp_hap_wf_s0_store), __ATTR(wf_s1, 0664, qpnp_hap_wf_s1_show, qpnp_hap_wf_s1_store), __ATTR(wf_s2, 0664, qpnp_hap_wf_s2_show, qpnp_hap_wf_s2_store), @@ -1809,196 +1429,96 @@ static struct device_attribute qpnp_hap_attrs[] = { qpnp_hap_ramp_test_data_store), __ATTR(min_max_test, 0664, qpnp_hap_min_max_test_data_show, qpnp_hap_min_max_test_data_store), - __ATTR(auto_res_mode, 0664, qpnp_hap_auto_res_mode_show, - qpnp_hap_auto_res_mode_store), - __ATTR(high_z_period, 0664, qpnp_hap_hi_z_period_show, - qpnp_hap_hi_z_period_store), - __ATTR(calib_period, 0664, qpnp_hap_calib_period_show, - qpnp_hap_calib_period_store), - __ATTR(override_auto_mode_config, 0664, - qpnp_hap_override_auto_mode_show, - qpnp_hap_override_auto_mode_store), - __ATTR(vmax_mv, 0664, qpnp_hap_vmax_show, qpnp_hap_vmax_store), }; -static int calculate_lra_code(struct qpnp_hap *hap) +static void calculate_lra_code(struct qpnp_hap *hap) { - u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0; - u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation; - u8 start_variation = AUTO_RES_ERROR_MAX, i; - u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; - int rc = 0; + u8 play_rate_code_lo, play_rate_code_hi; + int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1; + int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX; + int range; - rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), - &lra_drive_period_code_lo); - if (rc) { - pr_err("Error while reading RATE_CFG1 register\n"); - return rc; + qpnp_hap_read_reg(hap, &play_rate_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + qpnp_hap_read_reg(hap, &play_rate_code_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + + play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff); + + lra_init_freq = 200000 / play_rate_code; + range = (abs(lra_init_freq-235)*100)/235; + + lra_init_freq = (range < 5) ? lra_init_freq : 235; + + while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) { + freq_variation = (lra_init_freq * start_variation) / 100; + lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq - + freq_variation); + lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq + + freq_variation); + start_variation -= AUTO_RES_ERR_CAPTURE_RES; } - - rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), - &lra_drive_period_code_hi); - if (rc) { - pr_err("Error while reading RATE_CFG2 register\n"); - return rc; - } - - if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) { - pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); - return -EINVAL; - } - - lra_drive_period_code = - (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff); - lra_drive_frequency_hz = 200000 / lra_drive_period_code; - - while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) { - freq_variation = - (lra_drive_frequency_hz * start_variation) / 100; - adjusted_lra_play_rate_code[neg_idx++] = - 200000 / (lra_drive_frequency_hz - freq_variation); - adjusted_lra_play_rate_code[pos_idx--] = - 200000 / (lra_drive_frequency_hz + freq_variation); - start_variation -= AUTO_RES_ERROR_CAPTURE_RES; - } - - pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" - "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n" - "Calculated play rate code values are :\n", - lra_drive_period_code_lo, lra_drive_period_code_hi, - lra_drive_period_code, lra_drive_frequency_hz); - - for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i) - pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]); - - return 0; } static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) { int rc = 0; - u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; - u8 val, auto_res_mode_qwd; + u8 val; - if (hap->act_type != QPNP_HAP_LRA) - return 0; - - if (hap->pmic_subtype == PM660_SUBTYPE) - auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode == - QPNP_HAP_PM660_AUTO_RES_QWD); - else - auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode == - QPNP_HAP_AUTO_RES_QWD); - - /* - * Do not enable auto resonance if auto mode is enabled and auto - * resonance mode is QWD, meaning short pattern. - */ - if (hap->auto_mode && auto_res_mode_qwd && enable) { - pr_debug("auto_mode enabled, not enabling auto_res\n"); - return 0; - } - - if (!hap->correct_lra_drive_freq && !auto_res_mode_qwd) { - pr_debug("correct_lra_drive_freq: %d auto_res_mode_qwd: %d\n", - hap->correct_lra_drive_freq, auto_res_mode_qwd); - return 0; - } - - val = enable ? AUTO_RES_ENABLE : 0; - /* - * For auto resonance detection to work properly, sufficient back-emf - * has to be generated. In general, back-emf takes some time to build - * up. When the auto resonance mode is chosen as QWD, high-z will be - * applied for every LRA cycle and hence there won't be enough back-emf - * at the start-up. Hence, the motor needs to vibrate for few LRA cycles - * after the PLAY bit is asserted. Enable the auto resonance after - * 'time_required_to_generate_back_emf_us' is completed. - */ - if (enable) - usleep_range(back_emf_delay_us, back_emf_delay_us + 1); - - if (hap->pmic_subtype == PM660_SUBTYPE) - rc = qpnp_hap_masked_write_reg(hap, - QPNP_HAP_AUTO_RES_CTRL(hap->base), - QPNP_HAP_AUTO_RES_MASK, val); - else - rc = qpnp_hap_sec_masked_write_reg(hap, - QPNP_HAP_TEST2_REG(hap->base), - QPNP_HAP_AUTO_RES_MASK, val); + rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); if (rc < 0) return rc; + val &= QPNP_HAP_TEST2_AUTO_RES_MASK; if (enable) - hap->status_flags |= AUTO_RESONANCE_ENABLED; + val |= AUTO_RES_ENABLE; else - hap->status_flags &= ~AUTO_RESONANCE_ENABLED; + val |= AUTO_RES_DISABLE; - pr_debug("auto_res %sabled\n", enable ? "en" : "dis"); - return rc; + /* TEST2 is a secure access register */ + rc = qpnp_hap_sec_access(hap); + if (rc) + return rc; + + rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); + if (rc) + return rc; + + return 0; } static void update_lra_frequency(struct qpnp_hap *hap) { - u8 lra_auto_res[2], val; - u32 play_rate_code; - u16 rate_cfg; - int rc; + u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; + u32 temp = 0; + int lra_init_freq; - rc = qpnp_hap_read_mult_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), - lra_auto_res, 2); - if (rc < 0) { - pr_err("Error in reading LRA_AUTO_RES_LO/HI, rc=%d\n", rc); - return; + qpnp_hap_read_reg(hap, &lra_auto_res_lo, + QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); + qpnp_hap_read_reg(hap, &lra_auto_res_hi, + QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); + + lra_auto_res_hi = lra_auto_res_hi >> 4; + temp |= lra_auto_res_hi; + temp = temp << 8; + temp |= lra_auto_res_lo; + + lra_init_freq = 200000/temp; + + if ((abs(lra_init_freq-235)*100/235) < 5) { + lra_auto_res_lo = lra_auto_res_lo; + lra_auto_res_hi = lra_auto_res_hi; + } else{ + lra_auto_res_lo = 0x53; + lra_auto_res_hi = 0x3; } - play_rate_code = - (lra_auto_res[1] & 0xF0) << 4 | (lra_auto_res[0] & 0xFF); + if (lra_auto_res_lo && lra_auto_res_hi) { + qpnp_hap_write_reg(hap, &lra_auto_res_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); - pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", - lra_auto_res[0], lra_auto_res[1], play_rate_code); - - rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); - if (rc < 0) - return; - - /* - * If the drive period code read from AUTO_RES_LO and AUTO_RES_HI - * registers is more than the max limit percent variation or less - * than the min limit percent variation specified through DT, then - * auto-resonance is disabled. - */ - - if ((val & AUTO_RES_ERR_BIT) || - ((play_rate_code <= hap->drive_period_code_min_limit) || - (play_rate_code >= hap->drive_period_code_max_limit))) { - if (val & AUTO_RES_ERR_BIT) - pr_debug("Auto-resonance error %x\n", val); - else - pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n", - play_rate_code, - hap->drive_period_code_min_limit, - hap->drive_period_code_max_limit); - rc = qpnp_hap_auto_res_enable(hap, 0); - if (rc < 0) - pr_debug("Auto-resonance write failed\n"); - return; - } - - lra_auto_res[1] >>= 4; - rate_cfg = lra_auto_res[1] << 8 | lra_auto_res[0]; - if (hap->last_rate_cfg == rate_cfg) { - pr_debug("Same rate_cfg, skip updating\n"); - return; - } - - rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), - lra_auto_res, 2); - if (rc < 0) { - pr_err("Error in writing to RATE_CFG1/2, rc=%d\n", rc); - } else { - pr_debug("Update RATE_CFG with [0x%x]\n", rate_cfg); - hap->last_rate_cfg = rate_cfg; + qpnp_hap_write_reg(hap, &lra_auto_res_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); } } @@ -2006,10 +1526,15 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) { struct qpnp_hap *hap = container_of(timer, struct qpnp_hap, auto_res_err_poll_timer); + u8 val; ktime_t currtime; - if (!(hap->status_flags & AUTO_RESONANCE_ENABLED)) + qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + + if (val & AUTO_RES_ERR_BIT) { + schedule_work(&hap->auto_res_err_work); return HRTIMER_NORESTART; + } update_lra_frequency(hap); currtime = ktime_get(); @@ -2018,57 +1543,86 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) return HRTIMER_RESTART; } -static bool is_sw_lra_auto_resonance_control(struct qpnp_hap *hap) +static void correct_auto_res_error(struct work_struct *auto_res_err_work) { - if (hap->act_type != QPNP_HAP_LRA) - return false; + struct qpnp_hap *hap = container_of(auto_res_err_work, + struct qpnp_hap, auto_res_err_work); - if (hap->lra_hw_auto_resonance) - return false; + u8 lra_code_lo, lra_code_hi, disable_hap = 0x00; + static int lra_freq_index; + ktime_t currtime, remaining_time; + int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT; - if (!hap->correct_lra_drive_freq) - return false; + if (hrtimer_active(&hap->hap_timer)) { + remaining_time = hrtimer_get_remaining(&hap->hap_timer); + rem = (int)ktime_to_us(remaining_time); + } - if (hap->auto_mode && hap->play_mode == QPNP_HAP_BUFFER) - return false; + qpnp_hap_play(hap, 0); + qpnp_hap_write_reg(hap, &disable_hap, + QPNP_HAP_EN_CTL_REG(hap->base)); - return true; + lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK; + + qpnp_hap_write_reg(hap, &lra_code_lo, + QPNP_HAP_RATE_CFG1_REG(hap->base)); + + qpnp_hap_read_reg(hap, &lra_code_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + + lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK; + temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT; + lra_code_hi |= temp; + + qpnp_hap_write_reg(hap, &lra_code_hi, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + + lra_freq_index++; + + if (rem > 0) { + currtime = ktime_get(); + hap->state = 1; + hrtimer_forward(&hap->hap_timer, currtime, remaining_time); + schedule_work(&hap->work); + } } /* set api for haptics */ -static int qpnp_hap_set(struct qpnp_hap *hap, bool on) +static int qpnp_hap_set(struct qpnp_hap *hap, int on) { int rc = 0; + u8 val = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; if (hap->play_mode == QPNP_HAP_PWM) { - if (on) { + if (on) rc = pwm_enable(hap->pwm_info.pwm_dev); - if (rc < 0) - return rc; - } else { + else pwm_disable(hap->pwm_info.pwm_dev); - } } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { - rc = qpnp_hap_auto_res_enable(hap, 0); - if (rc < 0) - return rc; + if (hap->correct_lra_drive_freq || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) + qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); if (rc < 0) return rc; rc = qpnp_hap_play(hap, on); - if (rc < 0) - return rc; - rc = qpnp_hap_auto_res_enable(hap, 1); - if (rc < 0) - return rc; + if ((hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) || + hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { + usleep_range(AUTO_RES_ENABLE_TIMEOUT, + (AUTO_RES_ENABLE_TIMEOUT + 1)); - if (is_sw_lra_auto_resonance_control(hap)) { + rc = qpnp_hap_auto_res_enable(hap, 1); + if (rc < 0) + return rc; + } + if (hap->correct_lra_drive_freq) { /* * Start timer to poll Auto Resonance error bit */ @@ -2076,7 +1630,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, bool on) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_start(&hap->auto_res_err_poll_timer, ktime_set(0, timeout_ns), - HRTIMER_MODE_REL); + HRTIMER_MODE_REL); mutex_unlock(&hap->lock); } } else { @@ -2084,207 +1638,55 @@ static int qpnp_hap_set(struct qpnp_hap *hap, bool on) if (rc < 0) return rc; - if (is_sw_lra_auto_resonance_control(hap) && - (hap->status_flags & AUTO_RESONANCE_ENABLED)) - update_lra_frequency(hap); + if (hap->correct_lra_drive_freq) { + rc = qpnp_hap_read_reg(hap, &val, + QPNP_HAP_STATUS(hap->base)); + if (!(val & AUTO_RES_ERR_BIT)) + update_lra_frequency(hap); + } rc = qpnp_hap_mod_enable(hap, on); - if (rc < 0) - return rc; - - if (is_sw_lra_auto_resonance_control(hap)) + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) { hrtimer_cancel(&hap->auto_res_err_poll_timer); + calculate_lra_code(hap); + } } } return rc; } -static int qpnp_hap_auto_mode_config(struct qpnp_hap *hap, int time_ms) -{ - struct qpnp_hap_lra_ares_cfg ares_cfg; - enum qpnp_hap_mode old_play_mode; - u8 old_ares_mode; - u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN] = {0}; - u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN] = {0}; - int rc, vmax_mv; - - /* For now, this is for LRA only */ - if (hap->act_type == QPNP_HAP_ERM) - return 0; - - old_ares_mode = hap->ares_cfg.auto_res_mode; - old_play_mode = hap->play_mode; - pr_debug("auto_mode, time_ms: %d\n", time_ms); - if (time_ms <= 20) { - wave_samp[0] = QPNP_HAP_WAV_SAMP_MAX; - wave_samp[1] = QPNP_HAP_WAV_SAMP_MAX; - if (time_ms > 15) - wave_samp[2] = QPNP_HAP_WAV_SAMP_MAX; - - /* short pattern */ - rc = qpnp_hap_parse_buffer_dt(hap); - if (!rc) - rc = qpnp_hap_buffer_config(hap, wave_samp, true); - if (rc < 0) { - pr_err("Error in configuring buffer mode %d\n", - rc); - return rc; - } - - ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; - ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; - if (hap->pmic_subtype == PM660_SUBTYPE) { - ares_cfg.auto_res_mode = - QPNP_HAP_PM660_AUTO_RES_QWD; - ares_cfg.lra_qwd_drive_duration = 0; - ares_cfg.calibrate_at_eop = 0; - } else { - ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_QWD; - ares_cfg.lra_qwd_drive_duration = -EINVAL; - ares_cfg.calibrate_at_eop = -EINVAL; - } - - vmax_mv = QPNP_HAP_VMAX_MAX_MV; - rc = qpnp_hap_vmax_config(hap, vmax_mv, true); - if (rc < 0) - return rc; - - rc = qpnp_hap_brake_config(hap, brake_pat); - if (rc < 0) - return rc; - - /* enable play_irq for buffer mode */ - if (hap->play_irq >= 0 && !hap->play_irq_en) { - enable_irq(hap->play_irq); - hap->play_irq_en = true; - } - - hap->play_mode = QPNP_HAP_BUFFER; - hap->wave_shape = QPNP_HAP_WAV_SQUARE; - } else { - /* long pattern */ - ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; - if (hap->pmic_subtype == PM660_SUBTYPE) { - ares_cfg.auto_res_mode = - QPNP_HAP_PM660_AUTO_RES_ZXD; - ares_cfg.lra_res_cal_period = - QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; - ares_cfg.lra_qwd_drive_duration = 0; - ares_cfg.calibrate_at_eop = 1; - } else { - ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; - ares_cfg.lra_res_cal_period = - QPNP_HAP_RES_CAL_PERIOD_MAX; - ares_cfg.lra_qwd_drive_duration = -EINVAL; - ares_cfg.calibrate_at_eop = -EINVAL; - } - - vmax_mv = hap->vmax_mv; - rc = qpnp_hap_vmax_config(hap, vmax_mv, false); - if (rc < 0) - return rc; - - brake_pat[0] = 0x3; - rc = qpnp_hap_brake_config(hap, brake_pat); - if (rc < 0) - return rc; - - /* enable play_irq for direct mode */ - if (hap->play_irq >= 0 && hap->play_irq_en) { - disable_irq(hap->play_irq); - hap->play_irq_en = false; - } - - hap->play_mode = QPNP_HAP_DIRECT; - hap->wave_shape = QPNP_HAP_WAV_SINE; - } - - if (hap->override_auto_mode_config) { - rc = qpnp_hap_lra_auto_res_config(hap, NULL); - } else { - hap->ares_cfg.auto_res_mode = ares_cfg.auto_res_mode; - rc = qpnp_hap_lra_auto_res_config(hap, &ares_cfg); - } - - if (rc < 0) { - hap->ares_cfg.auto_res_mode = old_ares_mode; - return rc; - } - - rc = qpnp_hap_play_mode_config(hap); - if (rc < 0) { - hap->play_mode = old_play_mode; - return rc; - } - - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), - QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); - if (rc < 0) - return rc; - - return 0; -} - /* enable interface from timed output class */ -static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms) +static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value) { struct qpnp_hap *hap = container_of(dev, struct qpnp_hap, timed_dev); - bool state = !!time_ms; - ktime_t rem; - int rc; - - if (time_ms < 0) - return; mutex_lock(&hap->lock); - if (hap->state == state) { - if (state) { - rem = hrtimer_get_remaining(&hap->hap_timer); - if (time_ms > ktime_to_ms(rem)) { - time_ms = (time_ms > hap->timeout_ms ? - hap->timeout_ms : time_ms); - hrtimer_cancel(&hap->hap_timer); - hap->play_time_ms = time_ms; - hrtimer_start(&hap->hap_timer, - ktime_set(time_ms / 1000, - (time_ms % 1000) * 1000000), - HRTIMER_MODE_REL); - } - } - mutex_unlock(&hap->lock); - return; - } + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq) + hrtimer_cancel(&hap->auto_res_err_poll_timer); - hap->state = state; - if (!hap->state) { - hrtimer_cancel(&hap->hap_timer); + hrtimer_cancel(&hap->hap_timer); + + if (value == 0) { + if (hap->state == 0) { + mutex_unlock(&hap->lock); + return; + } + hap->state = 0; } else { - if (time_ms < 10) - time_ms = 10; - - if (hap->auto_mode) { - rc = qpnp_hap_auto_mode_config(hap, time_ms); - if (rc < 0) { - pr_err("Unable to do auto mode config\n"); - mutex_unlock(&hap->lock); - return; - } - } - - time_ms = (time_ms > hap->timeout_ms ? - hap->timeout_ms : time_ms); - hap->play_time_ms = time_ms; - hrtimer_start(&hap->hap_timer, - ktime_set(time_ms / 1000, - (time_ms % 1000) * 1000000), - HRTIMER_MODE_REL); + value = (value > hap->timeout_ms ? + hap->timeout_ms : value); + /*if value < 11ms,use overdrive*/ + qpnp_hap_wf_samp_store_all(dev, (value < 11?1:0)); + hap->state = 1; + hap->enable_time = value; } - + queue_work(vibqueue, &hap->work); mutex_unlock(&hap->lock); - schedule_work(&hap->work); } /* play pwm bytes */ @@ -2299,7 +1701,7 @@ int qpnp_hap_play_byte(u8 data, bool on) } if (hap->play_mode != QPNP_HAP_PWM) { - pr_err("only PWM mode is supported\n"); + dev_err(&hap->pdev->dev, "only PWM mode is supported\n"); return -EINVAL; } @@ -2340,7 +1742,8 @@ int qpnp_hap_play_byte(u8 data, bool on) if (rc) return rc; - pr_debug("data=0x%x duty_per=%d\n", data, duty_percent); + dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data, + duty_percent); rc = qpnp_hap_set(hap, true); @@ -2354,36 +1757,38 @@ static void qpnp_hap_worker(struct work_struct *work) struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 val = 0x00; - int rc; + int rc, reg_en = 0; - if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) { - rc = regulator_enable(hap->vcc_pon); - if (rc < 0) - pr_err("could not enable vcc_pon regulator rc=%d\n", - rc); - else - hap->vcc_pon_enabled = true; + if (hap->state) { + hrtimer_start(&hap->hap_timer, + ktime_set(hap->enable_time / 1000, + (hap->enable_time % 1000) * 1000000), HRTIMER_MODE_REL); + } + + if (hap->vcc_pon) { + reg_en = regulator_enable(hap->vcc_pon); + if (reg_en) + pr_err("%s: could not enable vcc_pon regulator\n", + __func__); } /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - if (hap->sc_count >= SC_MAX_COUNT) { - rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), - val); + if (hap->sc_duration == SC_MAX_DURATION) { + rc = qpnp_hap_write_reg(hap, &val, + QPNP_HAP_EN_CTL_REG(hap->base)); } else { if (hap->play_mode == QPNP_HAP_PWM) qpnp_hap_mod_enable(hap, hap->state); qpnp_hap_set(hap, hap->state); } - if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) { + if (hap->vcc_pon && !reg_en && (hap->state == 0)) { rc = regulator_disable(hap->vcc_pon); if (rc) - pr_err("could not disable vcc_pon regulator rc=%d\n", - rc); - else - hap->vcc_pon_enabled = false; + pr_err("%s: could not disable vcc_pon regulator\n", + __func__); } } @@ -2409,7 +1814,7 @@ static enum hrtimer_restart qpnp_hap_timer(struct hrtimer *timer) hap_timer); hap->state = 0; - schedule_work(&hap->work); + queue_work(vibqueue, &hap->work); return HRTIMER_NORESTART; } @@ -2434,7 +1839,7 @@ static int qpnp_haptic_suspend(struct device *dev) hrtimer_cancel(&hap->hap_timer); cancel_work_sync(&hap->work); /* turn-off haptic */ - qpnp_hap_set(hap, false); + qpnp_hap_set(hap, 0); return 0; } @@ -2445,24 +1850,51 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { - u8 val = 0; - int rc; + u8 reg = 0, unlock_val, error_value; + int rc, i, temp; + uint error_code = 0; - /* - * This denotes the percentage error in rc clock multiplied by 10 - */ - u8 rc_clk_err_percent_x10; - - /* Configure the CFG1 register for actuator type */ - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base), - QPNP_HAP_ACT_TYPE_MASK, hap->act_type); + /* Configure the ACTUATOR TYPE register */ + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_ACT_TYPE_MASK; + reg |= hap->act_type; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc) return rc; /* Configure auto resonance parameters */ - rc = qpnp_hap_lra_auto_res_config(hap, NULL); - if (rc) - return rc; + if (hap->act_type == QPNP_HAP_LRA) { + if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_AUTO_RES_MODE_MASK; + reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); + reg &= QPNP_HAP_LRA_HIGH_Z_MASK; + reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); + reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; + temp = fls(hap->lra_res_cal_period) - 1; + reg |= (temp - 2); + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); + if (rc) + return rc; + } else { + /* disable auto resonance for ERM */ + reg = 0x00; + + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); + if (rc) + return rc; + } /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); @@ -2470,12 +1902,23 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the VMAX register */ - rc = qpnp_hap_vmax_config(hap, hap->vmax_mv, false); + rc = qpnp_hap_vmax_config(hap); if (rc) return rc; /* Configure the ILIM register */ - rc = qpnp_hap_ilim_config(hap); + if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; + else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; + + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_ILIM_MASK; + temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); if (rc) return rc; @@ -2485,13 +1928,47 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the INTERNAL_PWM register */ - rc = qpnp_hap_int_pwm_config(hap); + if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; + temp = 0; + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + temp = 1; + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; + temp = 2; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; + temp = 3; + } + + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_INT_PWM_MASK; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); + if (rc) + return rc; + + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_INT_PWM_MASK; + reg |= temp; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); if (rc) return rc; /* Configure the WAVE SHAPE register */ - rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), - QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_WAV_SHAPE_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_WAV_SHAPE_MASK; + reg |= hap->wave_shape; + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_WAV_SHAPE_REG(hap->base)); if (rc) return rc; @@ -2505,87 +1982,91 @@ static int qpnp_hap_config(struct qpnp_hap *hap) else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; - hap->init_drive_period_code = - hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; + temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; /* - * The frequency of 19.2Mzhz RC clock is subject to variation. Currently - * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register - * present in their MISC block. This register holds the frequency error - * in 19.2 MHz RC clock. + * The frequency of 19.2Mzhz RC clock is subject to variation. + * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module + * holds the frequency error in 19.2Mhz RC clock */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq - && hap->misc_clk_trim_error_reg) { - pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code); + && hap->misc_trim_error_rc19p2_clk_reg_present) { + unlock_val = MISC_SEC_UNLOCK; + rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val); + if (rc) + dev_err(&hap->pdev->dev, + "Unable to do SEC_ACCESS rc:%d\n", rc); - /* - * Extract the 4 LSBs and multiply by 7 to get - * the %error in RC clock multiplied by 10 - */ - rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7; + regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, + &error_code); - /* - * If the TRIM register holds value less than 0x80, - * then there is a positive error in the RC clock. - * If the TRIM register holds value greater than or equal to - * 0x80, then there is a negative error in the RC clock. Bit 7 - * is the sign bit for error code. - * - * The adjusted play rate code is calculated as follows: - * LRA drive period code (RATE_CFG) = - * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100) - * - * This can be rewritten as: - * LRA drive period code (RATE_CFG) = - * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000) - * - * Since 200KHz * 1/LRA drive frequency is already calculated - * above we only do rest of the scaling here. - */ - if (hap->clk_trim_error_code & BIT(7)) - LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); + error_value = (error_code & 0x0F) * 7; + + if (error_code & 0x80) + temp = (temp * (1000 - error_value)) / 1000; else - LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); + temp = (temp * (1000 + error_value)) / 1000; } - pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code); + reg = temp & QPNP_HAP_RATE_CFG1_MASK; - val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; - rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val); + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_RATE_CFG1_REG(hap->base)); if (rc) return rc; - val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; - rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val); - if (rc) - return rc; - - hap->last_rate_cfg = hap->init_drive_period_code; - - if (hap->act_type == QPNP_HAP_LRA && - hap->perform_lra_auto_resonance_search) - calculate_lra_code(hap); - - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { - hap->drive_period_code_max_limit = - (hap->init_drive_period_code * (100 + - hap->drive_period_code_max_limit_percent_variation)) - / 100; - hap->drive_period_code_min_limit = - (hap->init_drive_period_code * (100 - - hap->drive_period_code_min_limit_percent_variation)) - / 100; - pr_debug("Drive period code max limit %x min limit %x\n", - hap->drive_period_code_max_limit, - hap->drive_period_code_min_limit); - } - - rc = qpnp_hap_brake_config(hap, NULL); + rc = qpnp_hap_read_reg(hap, ®, + QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc < 0) return rc; + reg &= QPNP_HAP_RATE_CFG2_MASK; + temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; + reg |= temp; + + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_RATE_CFG2_REG(hap->base)); + if (rc) + return rc; + + if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq) + calculate_lra_code(hap); + + /* Configure BRAKE register */ + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); + if (rc < 0) + return rc; + reg &= QPNP_HAP_BRAKE_MASK; + reg |= hap->en_brake; + rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); + if (rc) + return rc; + + if (hap->en_brake && hap->sup_brake_pat) { + for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { + hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; + temp = i << 1; + reg |= hap->brake_pat[i] << temp; + } + rc = qpnp_hap_write_reg(hap, ®, + QPNP_HAP_BRAKE_REG(hap->base)); + if (rc) + return rc; + } + + /* Cache enable control register */ + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); + if (rc < 0) + return rc; + hap->reg_en_ctl = reg; + + /* Cache play register */ + rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); + if (rc < 0) + return rc; + hap->reg_play = reg; if (hap->play_mode == QPNP_HAP_BUFFER) - rc = qpnp_hap_buffer_config(hap, NULL, false); + rc = qpnp_hap_buffer_config(hap); else if (hap->play_mode == QPNP_HAP_PWM) rc = qpnp_hap_pwm_config(hap); else if (hap->play_mode == QPNP_HAP_AUDIO) @@ -2594,37 +2075,21 @@ static int qpnp_hap_config(struct qpnp_hap *hap) if (rc) return rc; - /* setup play irq */ - if (hap->play_irq >= 0) { - rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, - NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play", - hap); - if (rc < 0) { - pr_err("Unable to request play(%d) IRQ(err:%d)\n", - hap->play_irq, rc); - return rc; - } - - /* use play_irq only for buffer mode */ - if (hap->play_mode != QPNP_HAP_BUFFER) { - disable_irq(hap->play_irq); - hap->play_irq_en = false; - } - } - /* setup short circuit irq */ - if (hap->sc_irq >= 0) { + if (hap->use_sc_irq) { rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq, - NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc", - hap); + NULL, qpnp_hap_sc_irq, + QPNP_IRQ_FLAGS, + "qpnp_sc_irq", hap); if (rc < 0) { - pr_err("Unable to request sc(%d) IRQ(err:%d)\n", + dev_err(&hap->pdev->dev, + "Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } } - hap->sc_count = 0; + hap->sc_duration = 0; return rc; } @@ -2633,46 +2098,18 @@ static int qpnp_hap_config(struct qpnp_hap *hap) static int qpnp_hap_parse_dt(struct qpnp_hap *hap) { struct platform_device *pdev = hap->pdev; - struct device_node *misc_node; struct property *prop; const char *temp_str; u32 temp; int rc; - if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) { - misc_node = of_parse_phandle(pdev->dev.of_node, - "qcom,pmic-misc", 0); - if (!misc_node) - return -EINVAL; - - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,misc-clk-trim-error-reg", &temp); - if (rc < 0) { - pr_err("Missing misc-clk-trim-error-reg\n"); - return rc; - } - - if (!temp || temp > 0xFF) { - pr_err("Invalid misc-clk-trim-error-reg\n"); - return -EINVAL; - } - - hap->misc_clk_trim_error_reg = temp; - rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg, - &hap->clk_trim_error_code); - if (rc < 0) { - pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc); - return -EPROBE_DEFER; - } - } - hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,timeout-ms", &temp); if (!rc) { hap->timeout_ms = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read timeout\n"); + dev_err(&pdev->dev, "Unable to read timeout\n"); return rc; } @@ -2685,136 +2122,68 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "lra") == 0) hap->act_type = QPNP_HAP_LRA; else { - pr_err("Invalid actuator type\n"); + dev_err(&pdev->dev, "Invalid actuator type\n"); return -EINVAL; } } else if (rc != -EINVAL) { - pr_err("Unable to read actuator type\n"); + dev_err(&pdev->dev, "Unable to read actuator type\n"); return rc; } if (hap->act_type == QPNP_HAP_LRA) { + hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; rc = of_property_read_string(pdev->dev.of_node, "qcom,lra-auto-res-mode", &temp_str); if (!rc) { - if (hap->pmic_subtype == PM660_SUBTYPE) { - hap->ares_cfg.auto_res_mode = - QPNP_HAP_PM660_AUTO_RES_QWD; - if (strcmp(temp_str, "zxd") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_PM660_AUTO_RES_ZXD; - else if (strcmp(temp_str, "qwd") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_PM660_AUTO_RES_QWD; - } else { - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_ZXD_EOP; - if (strcmp(temp_str, "none") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_NONE; - else if (strcmp(temp_str, "zxd") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_ZXD; - else if (strcmp(temp_str, "qwd") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_QWD; - else if (strcmp(temp_str, "max-qwd") == 0) - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_MAX_QWD; - else - hap->ares_cfg.auto_res_mode = - QPNP_HAP_AUTO_RES_ZXD_EOP; - } + if (strcmp(temp_str, "none") == 0) + hap->auto_res_mode = QPNP_HAP_AUTO_RES_NONE; + else if (strcmp(temp_str, "zxd") == 0) + hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD; + else if (strcmp(temp_str, "qwd") == 0) + hap->auto_res_mode = QPNP_HAP_AUTO_RES_QWD; + else if (strcmp(temp_str, "max-qwd") == 0) + hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; + else + hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; } else if (rc != -EINVAL) { - pr_err("Unable to read auto res mode\n"); + dev_err(&pdev->dev, "Unable to read auto res mode\n"); return rc; } - hap->ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; + hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; rc = of_property_read_string(pdev->dev.of_node, "qcom,lra-high-z", &temp_str); if (!rc) { if (strcmp(temp_str, "none") == 0) - hap->ares_cfg.lra_high_z = - QPNP_HAP_LRA_HIGH_Z_NONE; + hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; else if (strcmp(temp_str, "opt1") == 0) - hap->ares_cfg.lra_high_z = - QPNP_HAP_LRA_HIGH_Z_OPT1; + hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; else if (strcmp(temp_str, "opt2") == 0) - hap->ares_cfg.lra_high_z = - QPNP_HAP_LRA_HIGH_Z_OPT2; + hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT2; else - hap->ares_cfg.lra_high_z = - QPNP_HAP_LRA_HIGH_Z_OPT3; - - if (hap->pmic_subtype == PM660_SUBTYPE) { - if (strcmp(temp_str, "opt0") == 0) - hap->ares_cfg.lra_high_z = - QPNP_HAP_LRA_HIGH_Z_NONE; - } + hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { - pr_err("Unable to read LRA high-z\n"); + dev_err(&pdev->dev, "Unable to read LRA high-z\n"); return rc; } - hap->ares_cfg.lra_qwd_drive_duration = -EINVAL; - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,lra-qwd-drive-duration", - &hap->ares_cfg.lra_qwd_drive_duration); - - hap->ares_cfg.calibrate_at_eop = -EINVAL; - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,lra-calibrate-at-eop", - &hap->ares_cfg.calibrate_at_eop); - - hap->ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-res-cal-period", &temp); if (!rc) { - hap->ares_cfg.lra_res_cal_period = temp; + hap->lra_res_cal_period = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read cal period\n"); + dev_err(&pdev->dev, "Unable to read cal period\n"); return rc; } - hap->lra_hw_auto_resonance = - of_property_read_bool(pdev->dev.of_node, - "qcom,lra-hw-auto-resonance"); - - hap->perform_lra_auto_resonance_search = - of_property_read_bool(pdev->dev.of_node, - "qcom,perform-lra-auto-resonance-search"); - hap->correct_lra_drive_freq = of_property_read_bool(pdev->dev.of_node, "qcom,correct-lra-drive-freq"); - hap->drive_period_code_max_limit_percent_variation = 25; - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,drive-period-code-max-limit-percent-variation", &temp); - if (!rc) - hap->drive_period_code_max_limit_percent_variation = - (u8) temp; - - hap->drive_period_code_min_limit_percent_variation = 25; - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,drive-period-code-min-limit-percent-variation", &temp); - if (!rc) - hap->drive_period_code_min_limit_percent_variation = - (u8) temp; - - if (hap->ares_cfg.auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { - hap->time_required_to_generate_back_emf_us = - QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; - rc = of_property_read_u32(pdev->dev.of_node, - "qcom,time-required-to-generate-back-emf-us", - &temp); - if (!rc) - hap->time_required_to_generate_back_emf_us = - temp; - } else { - hap->time_required_to_generate_back_emf_us = 0; - } + hap->misc_trim_error_rc19p2_clk_reg_present = + of_property_read_bool(pdev->dev.of_node, + "qcom,misc-trim-error-rc19p2-clk-reg-present"); } rc = of_property_read_string(pdev->dev.of_node, @@ -2829,11 +2198,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "audio") == 0) hap->play_mode = QPNP_HAP_AUDIO; else { - pr_err("Invalid play mode\n"); + dev_err(&pdev->dev, "Invalid play mode\n"); return -EINVAL; } } else { - pr_err("Unable to read play mode\n"); + dev_err(&pdev->dev, "Unable to read play mode\n"); return rc; } @@ -2842,7 +2211,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->vmax_mv = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read vmax\n"); + dev_err(&pdev->dev, "Unable to read vmax\n"); return rc; } @@ -2851,7 +2220,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->ilim_ma = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read ILim\n"); + dev_err(&pdev->dev, "Unable to read ILim\n"); return rc; } @@ -2861,7 +2230,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->sc_deb_cycles = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read sc debounce\n"); + dev_err(&pdev->dev, "Unable to read sc debounce\n"); return rc; } @@ -2871,7 +2240,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->int_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read int pwm freq\n"); + dev_err(&pdev->dev, "Unable to read int pwm freq\n"); return rc; } @@ -2884,11 +2253,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "square") == 0) hap->wave_shape = QPNP_HAP_WAV_SQUARE; else { - pr_err("Unsupported wav shape\n"); + dev_err(&pdev->dev, "Unsupported wav shape\n"); return -EINVAL; } } else if (rc != -EINVAL) { - pr_err("Unable to read wav shape\n"); + dev_err(&pdev->dev, "Unable to read wav shape\n"); return rc; } @@ -2898,7 +2267,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_play_rate_us = temp; } else if (rc != -EINVAL) { - pr_err("Unable to read play rate\n"); + dev_err(&pdev->dev, "Unable to read play rate\n"); return rc; } @@ -2917,9 +2286,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) prop = of_find_property(pdev->dev.of_node, "qcom,brake-pattern", &temp); if (!prop) { - pr_info("brake pattern not found"); + dev_info(&pdev->dev, "brake pattern not found"); } else if (temp != QPNP_HAP_BRAKE_PAT_LEN) { - pr_err("Invalid len of brake pattern\n"); + dev_err(&pdev->dev, "Invalid len of brake pattern\n"); return -EINVAL; } else { hap->sup_brake_pat = true; @@ -2928,49 +2297,19 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } } - hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); - if (hap->play_irq < 0) - pr_warn("Unable to get play irq\n"); - - hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); - if (hap->sc_irq < 0) { - pr_err("Unable to get sc irq\n"); - return hap->sc_irq; + hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node, + "qcom,use-sc-irq"); + if (hap->use_sc_irq) { + hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); + if (hap->sc_irq < 0) { + dev_err(&pdev->dev, "Unable to get sc irq\n"); + return hap->sc_irq; + } } if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL)) hap->manage_pon_supply = true; - hap->auto_mode = of_property_read_bool(pdev->dev.of_node, - "qcom,lra-auto-mode"); - return 0; -} - -static int qpnp_hap_get_pmic_revid(struct qpnp_hap *hap) -{ - struct pmic_revid_data *pmic_rev_id; - struct device_node *revid_dev_node; - - revid_dev_node = of_parse_phandle(hap->pdev->dev.of_node, - "qcom,pmic-revid", 0); - if (!revid_dev_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - pmic_rev_id = get_revid_data(revid_dev_node); - if (IS_ERR_OR_NULL(pmic_rev_id)) { - pr_err("Unable to get pmic_revid rc=%ld\n", - PTR_ERR(pmic_rev_id)); - /* - * the revid peripheral must be registered, any failure - * here only indicates that the rev-id module has not - * probed yet. - */ - return -EPROBE_DEFER; - } - - hap->pmic_subtype = pmic_rev_id->pmic_subtype; - return 0; } @@ -2986,7 +2325,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) return -ENOMEM; hap->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hap->regmap) { - pr_err("Couldn't get parent's regmap\n"); + dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); return -EINVAL; } @@ -2994,7 +2333,8 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); if (rc < 0) { - pr_err("Couldn't find reg in node = %s rc = %d\n", + dev_err(&pdev->dev, + "Couldn't find reg in node = %s rc = %d\n", pdev->dev.of_node->full_name, rc); return rc; } @@ -3002,27 +2342,23 @@ static int qpnp_haptic_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hap); - rc = qpnp_hap_get_pmic_revid(hap); - if (rc) { - pr_err("Unable to check PMIC version rc=%d\n", rc); - return rc; - } - rc = qpnp_hap_parse_dt(hap); if (rc) { - pr_err("DT parsing failed\n"); + dev_err(&pdev->dev, "DT parsing failed\n"); return rc; } - spin_lock_init(&hap->bus_lock); rc = qpnp_hap_config(hap); if (rc) { - pr_err("hap config failed\n"); + dev_err(&pdev->dev, "hap config failed\n"); return rc; } mutex_init(&hap->lock); mutex_init(&hap->wf_lock); + + vibqueue = create_singlethread_workqueue("vibthread"); + INIT_WORK(&hap->work, qpnp_hap_worker); INIT_DELAYED_WORK(&hap->sc_work, qpnp_handle_sc_irq); init_completion(&hap->completion); @@ -3037,13 +2373,16 @@ static int qpnp_haptic_probe(struct platform_device *pdev) hap->timed_dev.get_time = qpnp_hap_get_time; hap->timed_dev.enable = qpnp_hap_td_enable; - hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - hap->auto_res_err_poll_timer.function = detect_auto_res_error; + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { + INIT_WORK(&hap->auto_res_err_work, correct_auto_res_error); + hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hap->auto_res_err_poll_timer.function = detect_auto_res_error; + } rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { - pr_err("timed_output registration failed\n"); + dev_err(&pdev->dev, "timed_output registration failed\n"); goto timed_output_fail; } @@ -3051,7 +2390,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = sysfs_create_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); if (rc < 0) { - pr_err("sysfs creation failed\n"); + dev_err(&pdev->dev, "sysfs creation failed\n"); goto sysfs_fail; } } @@ -3060,7 +2399,8 @@ static int qpnp_haptic_probe(struct platform_device *pdev) vcc_pon = regulator_get(&pdev->dev, "vcc_pon"); if (IS_ERR(vcc_pon)) { rc = PTR_ERR(vcc_pon); - pr_err("regulator get failed vcc_pon rc=%d\n", rc); + dev_err(&pdev->dev, + "regulator get failed vcc_pon rc=%d\n", rc); goto sysfs_fail; } hap->vcc_pon = vcc_pon; @@ -3077,7 +2417,8 @@ sysfs_fail: timed_output_dev_unregister(&hap->timed_dev); timed_output_fail: cancel_work_sync(&hap->work); - hrtimer_cancel(&hap->auto_res_err_poll_timer); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); mutex_destroy(&hap->lock); mutex_destroy(&hap->wf_lock); @@ -3095,7 +2436,8 @@ static int qpnp_haptic_remove(struct platform_device *pdev) &qpnp_hap_attrs[i].attr); cancel_work_sync(&hap->work); - hrtimer_cancel(&hap->auto_res_err_poll_timer); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); timed_output_dev_unregister(&hap->timed_dev); mutex_destroy(&hap->lock); diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 7604affe63a8..4d0069ec57d8 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -582,6 +582,8 @@ static enum msm_cpu cur_cpu; static int current_image; static uint32_t socinfo_format; +uint32_t chip_serial_num; + static struct socinfo_v0_1 dummy_socinfo = { .format = SOCINFO_VERSION(0, 1), .version = 1, @@ -1605,6 +1607,9 @@ int __init socinfo_init(void) boot_stats_init(); socinfo_print(); arch_read_hardware_id = msm_read_hardware_id; + + /*read serial number*/ + chip_serial_num = socinfo_get_serial_number(); socinfo_init_done = true; return 0; diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index ea94456ccef8..54e63593b2ef 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -40,6 +40,11 @@ #include #include "peripheral-loader.h" +#include +#include +#include +#include +#include #define DISABLE_SSR 0x9889deed /* If set to 0x9889deed, call to subsystem_restart_dev() returns immediately */ @@ -161,6 +166,7 @@ struct subsys_device { char wlname[64]; char error_buf[64]; struct work_struct device_restart_work; + struct work_struct crash_record_work; struct subsys_tracking track; void *notify; @@ -704,6 +710,141 @@ static struct subsys_device *find_subsys(const char *str) return dev ? to_subsys(dev) : NULL; } +static int val; + +static ssize_t proc_restart_level_all_read(struct file *p_file, + char __user *puser_buf, size_t count, loff_t *p_offset) +{ + ssize_t len = 0; + + len = copy_to_user(puser_buf, val?"1":"0", 1); + pr_info("the restart level switch is:%d\n", val); + return len; +} + +static ssize_t proc_restart_level_all_write(struct file *p_file, + const char __user *puser_buf, + size_t count, loff_t *p_offset) +{ + char temp[2] = {0}; + struct subsys_device *subsys; + int rc; + + if (copy_from_user(temp, puser_buf, 1)) + return -EFAULT; + + rc = kstrtoint(temp, 0, &val); + if (rc != 0) + return -EINVAL; + + if (!strncasecmp(&temp[0], "0", 1)) { + subsys = find_subsys("venus"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("a540_zap"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("ipa_fws"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("adsp"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("slpi"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("modem"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + subsys = find_subsys("spss"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SUBSYS_COUPLED; + + } else if (!strncasecmp(&temp[0], "1", 1)) { + subsys = find_subsys("venus"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("a540_zap"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("ipa_fws"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("adsp"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("slpi"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("modem"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + subsys = find_subsys("spss"); + if (!subsys) + return -ENODEV; + subsys->restart_level = RESET_SOC; + + } + + pr_info("write the restart level switch to :%d\n", val); + return count; +} + +static const struct file_operations restart_level_all_operations = { + .read = proc_restart_level_all_read, + .write = proc_restart_level_all_write, +}; + +static void init_restart_level_all_node(void) +{ + if (!proc_create("restart_level_all", 0644, NULL, + &restart_level_all_operations)){ + pr_err("%s : Failed to register proc interface\n", __func__); + } +} + +static int restart_level;/*system original val*/ +int op_restart_modem(void) +{ + struct subsys_device *subsys = find_subsys("modem"); + + if (!subsys) + return -ENODEV; + restart_level = subsys->restart_level; + subsys->restart_level = RESET_SUBSYS_COUPLED; + if (subsystem_restart("modem") == -ENODEV) + pr_err("%s: SSR call failed\n", __func__); + subsys->restart_level = restart_level; + return 0; +} +EXPORT_SYMBOL(op_restart_modem); + + static int subsys_start(struct subsys_device *subsys) { int ret; @@ -747,7 +888,7 @@ static int subsys_start(struct subsys_device *subsys) static void subsys_stop(struct subsys_device *subsys) { const char *name = subsys->desc->name; - + pr_info("[%s]: enter %s()\n", name, __func__); notify_each_subsys_device(&subsys, 1, SUBSYS_BEFORE_SHUTDOWN, NULL); if (!of_property_read_bool(subsys->desc->dev->of_node, "qcom,pil-force-shutdown")) { @@ -914,6 +1055,7 @@ void subsystem_put(void *subsystem) if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n", subsys->desc->name, __func__)) goto err_out; + pr_info("[%s]: enter %s()\n", subsys->desc->name, __func__); if (!--subsys->count) { subsys_stop(subsys); if (subsys->do_ramdump_on_put) @@ -1077,8 +1219,107 @@ static void device_restart_work_hdlr(struct work_struct *work) msleep(100); panic("subsys-restart: Resetting the SoC - %s crashed.", dev->desc->name); + } +#define KMSG_BUFSIZE 512 +#define MAX_RECORD_COUNT 16 +#define PARAM_CRASH_RECORD_SIZE 20 + +struct crash_index_list { + const char *crash_log_name; + const char *crash_index; +}; + +static struct crash_index_list crash_index[] = { + { "modem", "08"}, + { "slpi", "09"}, + { "adsp", "10"}, + { "cdsp", "11"}, + { "venus", "12"}, + { 0, 0 }, +}; + +void check_crash_restart(struct work_struct *work) +{ + struct subsys_device *dev = container_of(work, struct subsys_device, + crash_record_work); + const char *name = dev->desc->name; + char crash_time[19]; + char param_value[21]; + int split = 0, times = 0; + int rc = 0; + int i = 0; + int crash_record_count = 0; + int is_find_key_word = 0; + + struct timespec64 tspec; + struct rtc_time tm; + extern struct timezone sys_tz; + uint32 param_crash_record_offset = 0; + + for (i = 0; crash_index[i].crash_index; i++) { + if (!strcmp(name, crash_index[i].crash_log_name)) { + + /* Clean param_value buffer*/ + memset(param_value, 0, sizeof(param_value)); + + /* Get crash key word ID */ + strlcat(param_value, crash_index[i].crash_index, + sizeof(param_value)); + + if (!strcmp("08", crash_index[i].crash_index)) + add_restart_08_count(); + else + add_restart_other_count(); + + __getnstimeofday64(&tspec); + if (sys_tz.tz_minuteswest < 0 || + (tspec.tv_sec - sys_tz.tz_minuteswest*60) >= 0) + tspec.tv_sec -= sys_tz.tz_minuteswest * 60; + rtc_time_to_tm(tspec.tv_sec, &tm); + scnprintf(crash_time, sizeof(crash_time), + "%02d%02d%02d_%02d:%02d:%02d", + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + strlcat(param_value, crash_time, sizeof(param_value)); + + /* If find crash keyword, store crash record flag */ + is_find_key_word = 1; + } + } + + if (is_find_key_word) { + get_param_by_index_and_offset(9, 0x18, &crash_record_count, + sizeof(crash_record_count)); + param_crash_record_offset = 0x1C; + param_crash_record_offset = param_crash_record_offset + + (crash_record_count * PARAM_CRASH_RECORD_SIZE); + + pr_err("subsystem_restart: check_crash_restart: param_value = %s\n", + param_value); + + /* Write crash record to PARAM */ + split = sizeof(param_value)/4; + for (times = 0; times < split; times++) { + rc = set_param_by_index_and_offset(9, + param_crash_record_offset, + ¶m_value[times*4], 4); + param_crash_record_offset = + param_crash_record_offset + 4; + } + + /* Counter+1 */ + crash_record_count = crash_record_count + 1; + crash_record_count = crash_record_count % MAX_RECORD_COUNT; + set_param_by_index_and_offset(9, 0x18, &crash_record_count, + sizeof(crash_record_count)); + } + +} + + int subsystem_restart_dev(struct subsys_device *dev) { const char *name; @@ -1093,6 +1334,8 @@ int subsystem_restart_dev(struct subsys_device *dev) name = dev->desc->name; + schedule_work(&dev->crash_record_work); + /* * If a system reboot/shutdown is underway, ignore subsystem errors. * However, print a message so that we know that a subsystem behaved @@ -1263,7 +1506,7 @@ static int subsys_device_close(struct inode *inode, struct file *file) if (!subsys_dev) return -EINVAL; - + pr_info("[%s]: enter %s()\n", subsys_dev->desc->name, __func__); subsystem_put(subsys_dev); return 0; } @@ -1652,6 +1895,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc) snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name); wakeup_source_init(&subsys->ssr_wlock, subsys->wlname); + INIT_WORK(&subsys->crash_record_work, check_crash_restart); INIT_WORK(&subsys->work, subsystem_restart_wq_func); INIT_WORK(&subsys->device_restart_work, device_restart_work_hdlr); spin_lock_init(&subsys->track.s_lock); @@ -1811,6 +2055,8 @@ static int __init subsys_restart_init(void) if (ret) goto err_soc; + init_restart_level_all_node(); + return 0; err_soc: diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 3528cb08c78e..eb709c7902a4 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -703,7 +703,7 @@ static void init_watchdog_data(struct msm_watchdog_data *wdog_dd) wdog_dd->min_slack_ticks = UINT_MAX; wdog_dd->min_slack_ns = ULLONG_MAX; configure_bark_dump(wdog_dd); - timeout = (wdog_dd->bark_time * WDT_HZ)/1000; + timeout = (wdog_dd->bark_time * WDT_HZ)/1000+5; __raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME); __raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME); diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 4e4b39c26e89..568d5c6a2e20 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -122,6 +122,7 @@ enum pmic_arb_channel { struct pmic_arb_ver_ops; +static int resume_qpnp_kpdpwr_wakeup_flag = 0; struct apid_data { u16 ppid; u8 write_owner; @@ -536,6 +537,39 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) writel_relaxed(irq_mask, pa->intr + pa->ver_ops->irq_clear(apid)); } +static void init_qpnp_kpdpwr_resume_wakeup_flag(void) +{ + resume_qpnp_kpdpwr_wakeup_flag = 0; +} + +static int is_speedup_irq(struct irq_desc *desc, char *irq_name) +{ + return strstr(desc->action->name, irq_name) != NULL; +} + +static void set_qpnp_kpdpwr_resume_wakeup_flag(int irq) +{ + struct irq_desc *desc; + desc = irq_to_desc(irq); + + if (desc && desc->action && desc->action->name) { + if (is_speedup_irq(desc, "qpnp_kpdpwr_status")) { + resume_qpnp_kpdpwr_wakeup_flag = 1; + } + } +} + +int get_qpnp_kpdpwr_resume_wakeup_flag(void) +{ + int flag = resume_qpnp_kpdpwr_wakeup_flag; + + pr_debug("%s: flag = %d\n", __func__, flag); + /* Clear it for next calling */ + init_qpnp_kpdpwr_resume_wakeup_flag(); + + return flag; +} + static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid, bool show) { unsigned int irq; @@ -544,6 +578,7 @@ static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid, bool show) u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; u8 per = pa->apid_data[apid].ppid & 0xFF; + init_qpnp_kpdpwr_resume_wakeup_flag(); status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); while (status) { id = ffs(status) - 1; @@ -563,6 +598,7 @@ static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid, bool show) else if (desc->action && desc->action->name) name = desc->action->name; + set_qpnp_kpdpwr_resume_wakeup_flag(irq); pr_warn("spmi_show_resume_irq: %d triggered [0x%01x, 0x%02x, 0x%01x] %s\n", irq, sid, per, id, name); } else { diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c old mode 100644 new mode 100755 index 9e12b2ce3272..15c1ecc0b179 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -425,7 +425,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) other_free = global_page_state(NR_FREE_PAGES); - if (global_page_state(NR_SHMEM) + total_swapcache_pages() < + if (global_page_state(NR_SHMEM) + total_swapcache_pages() + + global_page_state(NR_UNEVICTABLE) < global_page_state(NR_FILE_PAGES) + zcache_pages()) other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - global_page_state(NR_SHMEM) - @@ -545,6 +546,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) " Total reserve is %ldkB\n" \ " Total free pages is %ldkB\n" \ " Total file cache is %ldkB\n" \ + " SHMEM is %ldkB\n" \ + " SwapCached is %ldkB\n" \ " Total zcache is %ldkB\n" \ " GFP mask is 0x%x\n", selected->comm, selected->pid, selected->tgid, @@ -561,6 +564,10 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) (long)(PAGE_SIZE / 1024), global_page_state(NR_FILE_PAGES) * (long)(PAGE_SIZE / 1024), + global_page_state(NR_SHMEM) * + (long)(PAGE_SIZE / 1024), + total_swapcache_pages() * + (long)(PAGE_SIZE / 1024), (long)zcache_pages() * (long)(PAGE_SIZE / 1024), sc->gfp_mask); diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index db49c33264d5..89f91ea4f99c 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -322,6 +322,7 @@ static struct of_device_id tsens_match[] = { {} }; + static struct tsens_tm_device *tsens_controller_is_present(void) { struct tsens_tm_device *tmdev_chip = NULL; @@ -862,6 +863,7 @@ static int tsens_tz_get_temp(struct thermal_zone_device *thermal, return -EINVAL; tmdev = tm_sensor->tm; + if (!tmdev) return -EINVAL; @@ -869,6 +871,9 @@ static int tsens_tz_get_temp(struct thermal_zone_device *thermal, if (rc) return rc; + if (tm_sensor->sensor_hw_num < 0 || tm_sensor->sensor_hw_num > 15) + return 0; + idx = tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].idx; tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].temp[idx%10] = *temp; tmdev->sensor_dbg_info[tm_sensor->sensor_hw_num].time_stmp[idx%10] = @@ -1416,6 +1421,9 @@ static void tsens_poll(struct work_struct *work) if (tmdev->tsens_critical_poll) { msleep(TSENS_DEBUG_POLL_MS); + /* Read sensor_status */ + mb(); + sensor_status_addr = TSENS_TM_SN_STATUS(tmdev->tsens_addr); spin_lock_irqsave(&tmdev->tsens_crit_lock, flags); @@ -2394,7 +2402,6 @@ static int tsens_tm_probe(struct platform_device *pdev) int rc, i; u32 tsens_num_sensors; struct tsens_tm_device *tmdev = NULL; - rc = of_property_read_u32(of_node, "qcom,sensors", &tsens_num_sensors); tmdev = devm_kzalloc(&pdev->dev, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index efaac5eb6592..815645c94334 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -39,6 +39,7 @@ #include #include #include +#include #define UART_MR1 0x0000 @@ -161,6 +162,22 @@ #define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ #define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4) +/*zyh we use dynamic add console , so we can't use __init __exit, +*this will cause can't find func +*/ +#ifdef __init +#undef __init +#endif + +#ifdef __exit +#undef __exit +#endif + +#define __init +#define __exit + + + enum { UARTDM_1P1 = 1, UARTDM_1P2, @@ -1729,6 +1746,31 @@ msm_serial_early_console_setup_dm(struct earlycon_device *device, OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", msm_serial_early_console_setup_dm); +/*Anderson-Config_UARTPIN_as_GPIO+[*/ + +struct oemconsole { + bool default_console; + bool force_console; + bool console_initialized; +}; + +static struct oemconsole oem_console = { + .default_console = false, + .force_console = false, + .console_initialized = false, +}; + + +static int __init parse_console_config(char *str) +{ + if(str != NULL){ + oem_console.default_console = true; + } + return 0; +} +early_param("console", parse_console_config); +/*Anderson-Config_UARTPIN_as_GPIO+]*/ + static struct uart_driver msm_uart_driver; static struct console msm_console = { @@ -1772,6 +1814,10 @@ static int msm_serial_probe(struct platform_device *pdev) struct uart_port *port; const struct of_device_id *id; int irq, line; + /*Anderson-Config_UARTPIN_as_GPIO+[*/ + struct pinctrl *pinctrl = NULL; + struct pinctrl_state *set_state = NULL; + /*Anderson-Config_UARTPIN_as_GPIO+]*/ if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -1790,6 +1836,24 @@ static int msm_serial_probe(struct platform_device *pdev) port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); + /*Anderson-Config_UARTPIN_as_GPIO+[*/ + pinctrl = devm_pinctrl_get(port->dev); + if (pinctrl != NULL) { + if (!oem_console.default_console && + !oem_console.force_console) { + set_state = pinctrl_lookup_state(pinctrl, + "uart_deactive"); + if(set_state != NULL) + pinctrl_select_state(pinctrl, set_state); + } else { + set_state = pinctrl_lookup_state(pinctrl, + "uart_active"); + if (set_state != NULL) + pinctrl_select_state(pinctrl, set_state); + } + } + /*Anderson-Config_UARTPIN_as_GPIO+]*/ + id = of_match_device(msm_uartdm_table, &pdev->dev); if (id) msm_port->is_uartdm = (unsigned long)id->data; @@ -1828,6 +1892,7 @@ static int msm_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); + msm_power(port, 3, 0); uart_remove_one_port(&msm_uart_driver, port); return 0; @@ -1873,11 +1938,29 @@ static struct platform_driver msm_platform_driver = { .pm = &msm_serial_pm_ops, }, }; - +static int msm_serial_pinctrl_init(void); static int __init msm_serial_init(void) { - int ret; + int ret = 0; + pr_err("%s\n", __func__); + if (oem_console.console_initialized == 1) { + pr_err("%s int=%d\n", __func__, + oem_console.console_initialized); + return 0; + } + + if (!oem_console.default_console && !oem_console.force_console) { + pr_err("don'tinit serial %s force_console=%d default_console=%d\n", + __func__, oem_console.force_console, + oem_console.default_console); + /* + *zyh we set serial gpio pown down, + *use register unregister will cause boot up slow + */ + msm_serial_pinctrl_init(); + return 0; + } ret = uart_register_driver(&msm_uart_driver); if (unlikely(ret)) return ret; @@ -1885,18 +1968,191 @@ static int __init msm_serial_init(void) ret = platform_driver_register(&msm_platform_driver); if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); - - pr_info("msm_serial: driver initialized\n"); + /*Anderson-Config_UARTPIN_as_GPIO*[*/ + if (!oem_console.default_console && !oem_console.force_console) { + platform_driver_unregister(&msm_platform_driver); + uart_unregister_driver(&msm_uart_driver); + pr_info("msm_platform_driver and msm_uart_driver unregister\n"); + } else { + oem_console.console_initialized = 1; + pr_info("msm_serial: driver initialized\n"); + } + /*Anderson-Config_UARTPIN_as_GPIO*]*/ return ret; } static void __exit msm_serial_exit(void) { + pr_err("%s\n", __func__); + + if (oem_console.console_initialized == 0) { + pr_err("msm_serial: driver already exit %s int=%d\n", + __func__, oem_console.console_initialized); + return; + } + oem_console.console_initialized = 0; platform_driver_unregister(&msm_platform_driver); uart_unregister_driver(&msm_uart_driver); } +static int msm_serial_pinctrl_probe(struct platform_device *pdev) +{ + + struct pinctrl *pinctrl = NULL; + struct pinctrl_state *set_state = NULL; + struct device *dev = &pdev->dev; + + pr_err("%s\n", __func__); + pinctrl = devm_pinctrl_get(dev); + + if (pinctrl != NULL) { + + set_state = pinctrl_lookup_state( + pinctrl, "uart_pinctrl_deactive"); + + if (set_state != NULL) + pinctrl_select_state(pinctrl, set_state); + + devm_pinctrl_put(pinctrl); + } + return 0; +} + +static int msm_serial_pinctrl_remove(struct platform_device *pdev) +{ + return 0; +} + + +static const struct of_device_id oem_serial_pinctrl_of_match[] = { + { .compatible = "oem,oem_serial_pinctrl" }, + {} +}; + + +static struct platform_driver msm_platform_serial_pinctrl_driver = { + .remove = msm_serial_pinctrl_remove, + .probe = msm_serial_pinctrl_probe, + .driver = { + .name = "oem_serial_pinctrl", + .of_match_table = oem_serial_pinctrl_of_match, + }, +}; + +static int msm_serial_pinctrl_init(void) +{ + int ret = 0; + + pr_err("%s\n", __func__); + + ret = platform_driver_register(&msm_platform_serial_pinctrl_driver); + + return ret; +} + +#define OEM_SERIAL_MAGIC 0xEA +#define OEM_FORCE_SERIAL_OPEN _IOW(OEM_SERIAL_MAGIC, 0x01, int) +#define OEM_FORCE_SERIAL_CLOSE _IOW(OEM_SERIAL_MAGIC, 0x02, int) +#define SERIAL_CMDLINE "ttyMSM0,115200,n8" + +struct miscdevice fore_serial_device; +char oem_force_cmdline_str[60]; + +int msm_serial_oem_init(void) +{ + int ret = 0; + + pr_err("%s\n", __func__); + oem_console.force_console = true; + memcpy(oem_force_cmdline_str, SERIAL_CMDLINE, sizeof(SERIAL_CMDLINE)); + force_oem_console_setup(&oem_force_cmdline_str[0]); + msm_serial_init(); + return ret; +} +EXPORT_SYMBOL(msm_serial_oem_init); + +void msm_serial_oem_exit(void) +{ + pr_err("%s\n", __func__); + msm_serial_exit(); + oem_console.force_console = false; +} +EXPORT_SYMBOL(msm_serial_oem_exit); + +static ssize_t oem_force_serial_dev_read +(struct file *filp, char __user *buf, size_t count, loff_t *offset) +{ + return 0; +} + +static ssize_t oem_force_serial_dev_write +(struct file *filp, const char __user *buf, size_t count, loff_t *offset) +{ + return 0; +} + +static int oem_force_serial_dev_open +(struct inode *inode, struct file *filp) +{ + return 0; +} + +static long oem_force_serial_dev_ioctl +(struct file *filp, unsigned int cmd, unsigned long arg) +{ + pr_info("%s : cmd = %d, arg = %ld\n", __func__, _IOC_NR(cmd), arg); + switch (cmd) { + case OEM_FORCE_SERIAL_OPEN: + msm_serial_oem_init(); + break; + case OEM_FORCE_SERIAL_CLOSE: + msm_serial_oem_exit(); + break; + } + return 0; +} + +static const struct file_operations oem_force_serial_dev_fops = { + .owner = THIS_MODULE, + .read = oem_force_serial_dev_read, + .write = oem_force_serial_dev_write, + .open = oem_force_serial_dev_open, + .unlocked_ioctl = oem_force_serial_dev_ioctl, +}; + +static int oem_force_serial_probe(struct platform_device *pdev) +{ + int ret = 0; + + pr_err("%s\n", __func__); + fore_serial_device.minor = MISC_DYNAMIC_MINOR; + fore_serial_device.name = "oem_force_serial"; + fore_serial_device.fops = &oem_force_serial_dev_fops; + + ret = misc_register(&fore_serial_device); + + if (ret) + pr_err("%s : misc_register failed\n", __func__); + + return ret; +} + +static const struct of_device_id oem_force_serial_of_match[] = { + {.compatible = "oem,force_serial" }, + {} +}; +MODULE_DEVICE_TABLE(of, oem_force_serial_of_match); + +static struct platform_driver oem_force_serial_driver = { + .driver = { + .name = "force_serial", + .owner = THIS_MODULE, + .of_match_table = oem_force_serial_of_match, + }, + .probe = oem_force_serial_probe, +}; +module_platform_driver(oem_force_serial_driver); module_init(msm_serial_init); module_exit(msm_serial_exit); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 55322469084d..ff536b53ae2b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1807,6 +1807,15 @@ static int autosuspend_check(struct usb_device *udev) * or else their drivers don't support autosuspend * and so they are permanently active. */ + + /* 2016/07/01, Avoid null pointer if otg device + * filesystem has problem + */ + if (intf == NULL) { + dev_err(&udev->dev, "%s intf = NULL\n", + __func__); + return -EIO; + } if (intf->dev.power.disable_depth) continue; if (atomic_read(&intf->dev.power.usage_count) > 0) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1e25aa251c05..c1abe37bace7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4268,11 +4268,9 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) if (hub) connect_type = hub->ports[udev->portnum - 1]->connect_type; - if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) || - connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { - udev->usb2_hw_lpm_allowed = 1; - usb_set_usb2_hardware_lpm(udev, 1); - } + if(udev->bos == NULL) + return; + } static int hub_enable_device(struct usb_device *udev) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3191825710af..98ef66b338ae 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1220,6 +1220,9 @@ static int dwc3_probe(struct platform_device *pdev) if (dwc->maximum_speed == USB_SPEED_UNKNOWN) dwc->max_hw_supp_speed = dwc->maximum_speed = USB_SPEED_SUPER; + pr_info("Force USB running as High speed"); + dwc->max_hw_supp_speed = dwc->maximum_speed = USB_SPEED_HIGH; + dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 2c00b3596055..48bef4b349e4 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -800,10 +800,9 @@ const struct file_operations dwc3_ep_trb_list_fops = { .llseek = seq_lseek, .release = single_release, }; - -static unsigned int ep_addr_rxdbg_mask = 1; +static unsigned int ep_addr_rxdbg_mask = 0xFF; module_param(ep_addr_rxdbg_mask, uint, S_IRUGO | S_IWUSR); -static unsigned int ep_addr_txdbg_mask = 1; +static unsigned int ep_addr_txdbg_mask = 0xFF; module_param(ep_addr_txdbg_mask, uint, S_IRUGO | S_IWUSR); /* Maximum debug message length */ diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index dc06f4818e39..f5e373808a63 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -86,6 +86,7 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu"); #define CGCTL_REG (QSCRATCH_REG_OFFSET + 0x28) #define PWR_EVNT_IRQ_STAT_REG (QSCRATCH_REG_OFFSET + 0x58) #define PWR_EVNT_IRQ_MASK_REG (QSCRATCH_REG_OFFSET + 0x5C) +#define QSCRATCH_USB30_STS_REG (QSCRATCH_REG_OFFSET + 0xF8) #define PWR_EVNT_POWERDOWN_IN_P3_MASK BIT(2) #define PWR_EVNT_POWERDOWN_OUT_P3_MASK BIT(3) @@ -1954,8 +1955,19 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc) break; usleep_range(20, 30); } - if (!(reg & PWR_EVNT_LPM_IN_L2_MASK)) - dev_err(mdwc->dev, "could not transition HS PHY to L2\n"); + if (!(reg & PWR_EVNT_LPM_IN_L2_MASK)) { + dbg_event(0xFF, "PWR_EVNT_LPM", + dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG)); + dbg_event(0xFF, "QUSB_STS", + dwc3_msm_read_reg(mdwc->base, QSCRATCH_USB30_STS_REG)); + /* Mark fatal error for host mode or USB bus suspend case */ + if (mdwc->in_host_mode || (mdwc->vbus_active + && mdwc->otg_state == OTG_STATE_B_SUSPEND)) { + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + dev_err(mdwc->dev, "could not transition HS PHY to L2\n"); + return -EBUSY; + } + } /* Clear L2 event bit */ dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG, diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 24ecbc469eb6..b5b3ae0e6d5f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -37,10 +37,25 @@ #include "io.h" #include "debug.h" +#include static bool enable_dwc3_u1u2; module_param(enable_dwc3_u1u2, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(enable_dwc3_u1u2, "Enable support for U1U2 low power modes"); +static struct notify_usb_enumeration_status + *usb_enumeration_status = NULL; + +void regsister_notify_usb_enumeration_status( + struct notify_usb_enumeration_status *status) +{ + if (usb_enumeration_status) { + usb_enumeration_status = status; + pr_err("multiple usb_enumeration_status called\n"); + } else { + usb_enumeration_status = status; + } +} +EXPORT_SYMBOL(regsister_notify_usb_enumeration_status); static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep); static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, @@ -775,6 +790,10 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: + if (usb_enumeration_status + && usb_enumeration_status->notify_usb_enumeration) { + usb_enumeration_status->notify_usb_enumeration(true); + } dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS"); ret = dwc3_ep0_set_address(dwc, ctrl); break; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f6117ac0e301..85cc54958046 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2872,7 +2872,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0); dwc3_usb3_phy_suspend(dwc, false); - usb_gadget_vbus_draw(&dwc->gadget, 100); dwc3_reset_gadget(dwc); dbg_event(0xFF, "BUS RST", 0); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index bf9730884235..7679c816b574 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2350,7 +2350,6 @@ void composite_suspend(struct usb_gadget *gadget) cdev->suspended = 1; spin_unlock_irqrestore(&cdev->lock, flags); - usb_gadget_vbus_draw(gadget, 2); } void composite_resume(struct usb_gadget *gadget) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index f97193011bed..c52562b5a7a4 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "u_fs.h" #include "u_f.h" @@ -44,6 +46,15 @@ #define NUM_PAGES 10 /* # of pages for ipc logging */ +#define PM_QOS_REQUEST_SIZE 0xF000 /* > 4096*/ +#define ADB_QOS_TIMEOUT 500000 +#define ADB_PULL_PUSH_TIMEOUT 1000 + +static struct pm_qos_request adb_little_cpu_qos; +static struct hrtimer ffs_op_timer; +static bool lpm_flg = true; +static bool ffs_op_flg = true; + static void *ffs_ipc_log; #define ffs_log(fmt, ...) do { \ if (ffs_ipc_log) \ @@ -614,15 +625,20 @@ static int ffs_ep0_open(struct inode *inode, struct file *file) if (ret < 0) return ret; - if (unlikely(ffs->state == FFS_CLOSING)) + if (unlikely(ffs->state == FFS_CLOSING)) { + pr_err("FFS_CLOSING!\n"); return -EBUSY; + } smp_mb__before_atomic(); - if (atomic_read(&ffs->opened)) + if (atomic_read(&ffs->opened)) { + pr_err("ep0 is already opened!\n"); return -EBUSY; + } file->private_data = ffs; ffs_data_opened(ffs); + pr_info("ep0_open success!\n"); return 0; } @@ -1091,6 +1107,27 @@ error: return ret; } +static enum hrtimer_restart ffs_op_timeout(struct hrtimer *timer) +{ + static int cnt; + + /* wait 5s to close */ + if (!ffs_op_flg) + cnt = cnt + 1; + if (cnt > 5) { + pr_info("ffs_op_timeout, close lpm_disable\n"); + msm_cpuidle_set_sleep_disable(false); + cnt = 0; + lpm_flg = false; + return HRTIMER_NORESTART; + } + + hrtimer_start(&ffs_op_timer, + ms_to_ktime(ADB_PULL_PUSH_TIMEOUT), + HRTIMER_MODE_REL); + return HRTIMER_RESTART; +} + static int ffs_epfile_open(struct inode *inode, struct file *file) { @@ -1160,6 +1197,8 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) { struct ffs_io_data io_data, *p = &io_data; ssize_t res; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + bool adb_write_flag = false; ENTER(); @@ -1181,10 +1220,30 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) + if (p->aio) { kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } else { + if ((strcmp(epfile->name, "ep1") == 0) + || (strcmp(epfile->name, "ep2") == 0)) + adb_write_flag = true; + + if ((p->data.count & PM_QOS_REQUEST_SIZE) && adb_write_flag) { + if (!lpm_flg) { + msm_cpuidle_set_sleep_disable(true); + hrtimer_start(&ffs_op_timer, + ms_to_ktime(ADB_PULL_PUSH_TIMEOUT), + HRTIMER_MODE_REL); + } + lpm_flg = true; + ffs_op_flg = true; + pm_qos_update_request_timeout(&adb_little_cpu_qos, + (MAX_CPUFREQ - 4), ADB_QOS_TIMEOUT); + } + } res = ffs_epfile_io(kiocb->ki_filp, p); + if (ffs_op_flg) + ffs_op_flg = false; if (res == -EIOCBQUEUED) return res; if (p->aio) @@ -1201,6 +1260,8 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) { struct ffs_io_data io_data, *p = &io_data; ssize_t res; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; + bool adb_read_flag = false; ENTER(); @@ -1231,10 +1292,31 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) + if (p->aio) { kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } else { + if ((strcmp(epfile->name, "ep1") == 0) + || (strcmp(epfile->name, "ep2") == 0)) + adb_read_flag = true; + + if ((p->data.count & PM_QOS_REQUEST_SIZE) + && adb_read_flag) { + if (!lpm_flg) { + msm_cpuidle_set_sleep_disable(true); + hrtimer_start(&ffs_op_timer, + ms_to_ktime(ADB_PULL_PUSH_TIMEOUT), + HRTIMER_MODE_REL); + } + lpm_flg = true; + ffs_op_flg = true; + pm_qos_update_request_timeout(&adb_little_cpu_qos, + (MAX_CPUFREQ - 4), ADB_QOS_TIMEOUT); + } + } res = ffs_epfile_io(kiocb->ki_filp, p); + if (ffs_op_flg) + ffs_op_flg = false; if (res == -EIOCBQUEUED) return res; @@ -1983,6 +2065,9 @@ static int ffs_epfiles_create(struct ffs_data *ffs) ffs->epfiles = epfiles; + pm_qos_add_request(&adb_little_cpu_qos, PM_QOS_C0_CPUFREQ_MIN, + MIN_CPUFREQ); + ffs_log("exit: eps_count %u state %d setup_state %d flag %lu", count, ffs->state, ffs->setup_state, ffs->flags); @@ -2008,6 +2093,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) } kfree(epfiles); + pm_qos_remove_request(&adb_little_cpu_qos); ffs_log("exit"); } @@ -3428,6 +3514,10 @@ static int ffs_func_bind(struct usb_configuration *c, if (ret && !--ffs_opts->refcnt) functionfs_unbind(func->ffs); + lpm_flg = false; + ffs_op_flg = false; + hrtimer_init(&ffs_op_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ffs_op_timer.function = ffs_op_timeout; ffs_log("exit: ret %d", ret); return ret; @@ -3988,6 +4078,11 @@ static void ffs_func_unbind(struct usb_configuration *c, func->interfaces_nums = NULL; ffs_event_add(ffs, FUNCTIONFS_UNBIND); + hrtimer_cancel(&ffs_op_timer); + if (lpm_flg) + msm_cpuidle_set_sleep_disable(false); + lpm_flg = false; + ffs_op_flg = false; ffs_log("exit: state %d setup_state %d flag %lu", ffs->state, ffs->setup_state, ffs->flags); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 7edc981c78b6..f28c252c9731 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -1231,6 +1231,248 @@ static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) return 8; } +/*2016/09/21, CD-ROM and VID customized + *add for cdrom support MAC OSX + */ +static void _lba_to_msf(u8 *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static int _read_toc_raw(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + u8 *buf = (u8 *) bh->buf; + + u8 *q; + int len; + + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa0; /* lead-in */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* first track */ + *q++ = 0x00; /* disk type */ + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa1; + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* last track */ + *q++ = 0x00; + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa2; /* lead-out */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + if (msf) { + *q++ = 0; /* reserved */ + _lba_to_msf(q, curlun->num_sectors); + q += 3; + } else { + put_unaligned_be32(curlun->num_sectors, q); + q += 4; + } + + *q++ = 1; /* session number */ + *q++ = 0x14; /* ADR, control */ + *q++ = 0; /* track number */ + *q++ = 1; /* point */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + if (msf) { + *q++ = 0; + _lba_to_msf(q, 0); + q += 3; + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + } + + len = q - buf; + put_unaligned_be16(len - 2, buf); + + return len; +} + +static void cd_data_to_raw(u8 *buf, int lba) +{ + /* sync bytes */ + buf[0] = 0x00; + memset(buf + 1, 0xff, 10); + buf[11] = 0x00; + buf += 12; + /* MSF */ + _lba_to_msf(buf, lba); + buf[3] = 0x01; /* mode 1 data */ + buf += 4; + /* data */ + buf += 2048; + /* XXX: ECC not computed */ + memset(buf, 0, 288); +} + +static int do_read_cd(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset, file_offset_tmp; + unsigned int amount; + unsigned int partial_page; + ssize_t nread; + + u32 nb_sectors, transfer_request; + + nb_sectors = (common->cmnd[6] << 16) | + (common->cmnd[7] << 8) | common->cmnd[8]; + lba = get_unaligned_be32(&common->cmnd[2]); + + if (nb_sectors == 0) + return 0; + + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + transfer_request = common->cmnd[9]; + if ((transfer_request & 0xf8) == 0xf8) { + file_offset = ((loff_t) lba) << 11; + /* read all data - 2352 byte */ + amount_left = 2352; + } else { + file_offset = ((loff_t) lba) << 9; + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + } + + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. + */ + amount = min(amount_left, FSG_BUFLEN); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common, true); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. + */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + if ((transfer_request & 0xf8) == 0xf8) { + nread = vfs_read(curlun->filp, + ((char __user *)bh->buf)+16, + amount, &file_offset_tmp); + } else { + nread = vfs_read(curlun->filp, + (char __user *)bh->buf, + amount, &file_offset_tmp); + } + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + if (!start_in_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + if ((transfer_request & 0xf8) == 0xf8) + cd_data_to_raw(bh->buf, lba); + + return -EIO; /* No default reply */ +} +/*end add for cdrom support MAC OSX*/ + static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) { struct fsg_lun *curlun = common->curlun; @@ -1238,12 +1480,25 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) int start_track = common->cmnd[6]; u8 *buf = (u8 *)bh->buf; + /*2016/09/21, CD-ROM and VID customized + *add for cdrom support MAC OSX + */ + int format = (common->cmnd[9] & 0xC0) >> 6; + /*end add for cdrom support MAC OSX*/ + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ start_track > 1) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } + /*2016/09/21, CD-ROM and VID customized + *add for cdrom support MAC OSX + */ + if (format == 2) + return _read_toc_raw(common, bh); + /*end add for cdrom support MAC OSX*/ + memset(buf, 0, 20); buf[1] = (20-2); /* TOC data length */ buf[2] = 1; /* First track number */ @@ -1989,12 +2244,30 @@ static int do_scsi_command(struct fsg_common *common) goto unknown_cmnd; common->data_size_from_cmnd = get_unaligned_be16(&common->cmnd[7]); + + /*2016/09/21, CD-ROM and VID customized + *add for cdrom support MAC OSX + */ reply = check_command(common, 10, DATA_DIR_TO_HOST, - (7<<6) | (1<<1), 1, + (0xf<<6) | (1<<1), 1, "READ TOC"); if (reply == 0) reply = do_read_toc(common, bh); break; + /*2016/09/21, CD-ROM and VID customized + *add for cdrom support MAC OSX + */ + case READ_CD: + common->data_size_from_cmnd = ((common->cmnd[6] << 16) + | (common->cmnd[7] << 8) + | (common->cmnd[8])) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (0xf<<2) | (7<<7), 1, + "READ CD"); + if (reply == 0) + reply = do_read_cd(common); + break; + /*end add for cdrom support MAC OSX*/ case READ_FORMAT_CAPACITIES: common->data_size_from_cmnd = @@ -2268,6 +2541,7 @@ reset: if (common->fsg) { fsg = common->fsg; + pr_err("%s:disable endpoints here\n", __func__); for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; @@ -2281,18 +2555,6 @@ reset: } } - /* Disable the endpoints */ - if (fsg->bulk_in_enabled) { - usb_ep_disable(fsg->bulk_in); - fsg->bulk_in_enabled = 0; - } - if (fsg->bulk_out_enabled) { - usb_ep_disable(fsg->bulk_out); - fsg->bulk_out_enabled = 0; - } - - /* allow usb LPM after eps are disabled */ - usb_gadget_autopm_put_async(common->gadget); common->fsg = NULL; wake_up(&common->fsg_wait); } @@ -2304,28 +2566,6 @@ reset: common->fsg = new_fsg; fsg = common->fsg; - /* Enable the endpoints */ - rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); - if (rc) - goto reset; - rc = usb_ep_enable(fsg->bulk_in); - if (rc) - goto reset; - fsg->bulk_in->driver_data = common; - fsg->bulk_in_enabled = 1; - - rc = config_ep_by_speed(common->gadget, &(fsg->function), - fsg->bulk_out); - if (rc) - goto reset; - rc = usb_ep_enable(fsg->bulk_out); - if (rc) - goto reset; - fsg->bulk_out->driver_data = common; - fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); - clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); - /* Allocate the requests */ for (i = 0; i < common->fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; @@ -2341,7 +2581,8 @@ reset: bh->inreq->complete = bulk_in_complete; bh->outreq->complete = bulk_out_complete; } - + pr_err("%s:increment pm_usage count num_buffers=%d\n", __func__ + , common->fsg_num_buffers); common->running = 1; for (i = 0; i < ARRAY_SIZE(common->luns); ++i) if (common->luns[i]) @@ -2356,20 +2597,84 @@ reset: static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct fsg_dev *fsg = fsg_from_func(f); + int rc; + fsg->common->new_fsg = fsg; + + pr_err("%s:increment pm_usage counter\n", __func__); + if (!fsg->common || !fsg->common->gadget) + pr_err("%s: NULL here\n", __func__); + /* prevents usb LPM until thread runs to completion */ usb_gadget_autopm_get_async(fsg->common->gadget); + /* Enable the endpoints */ + rc = config_ep_by_speed(fsg->common->gadget, &(fsg->function), + fsg->bulk_in); + if (rc) + goto err_exit; + rc = usb_ep_enable(fsg->bulk_in); + if (rc) + goto err_exit; + fsg->bulk_in->driver_data = fsg->common; + fsg->bulk_in_enabled = 1; + + rc = config_ep_by_speed(fsg->common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset_bulk_int; + + rc = usb_ep_enable(fsg->bulk_out); + if (rc) + goto reset_bulk_int; + + fsg->bulk_out->driver_data = fsg->common; + fsg->bulk_out_enabled = 1; + fsg->common->bulk_out_maxpacket = + usb_endpoint_maxp(fsg->bulk_out->desc); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return USB_GADGET_DELAYED_STATUS; + +reset_bulk_int: + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in->driver_data = NULL; + fsg->bulk_in_enabled = 0; +err_exit: + return rc; } static void fsg_disable(struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); + + pr_err("%s:disable endpoints\n", __func__); + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in->driver_data = NULL; + fsg->bulk_in_enabled = 0; + } + + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out->driver_data = NULL; + fsg->bulk_out_enabled = 0; + } + + pr_err("%s:eps are disabled\n", __func__); + pr_err("%s:disabled endpoints\n", __func__); fsg->common->new_fsg = NULL; + pr_err("%s:cur_state=%d\n", __func__, fsg->common->state); raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + /* allow usb LPM after eps are disabled */ + usb_gadget_autopm_put_async(fsg->common->gadget); + + pr_err("%s:\n", __func__); } @@ -2383,6 +2688,7 @@ static void handle_exception(struct fsg_common *common) struct fsg_lun *curlun; unsigned int exception_req_tag; + pr_err("%s:current_state= %d\n", __func__, common->state); /* * Clear the existing signals. Anything but SIGUSR1 is converted * into a high-priority EXIT exception. @@ -2402,6 +2708,8 @@ static void handle_exception(struct fsg_common *common) if (likely(common->fsg)) { for (i = 0; i < common->fsg_num_buffers; ++i) { bh = &common->buffhds[i]; + pr_err("%s:dequeue reqs %d inreq_busy=%d outreq_busy=%d\n", + __func__, i, bh->inreq_busy, bh->outreq_busy); if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); if (bh->outreq_busy) @@ -2461,6 +2769,7 @@ static void handle_exception(struct fsg_common *common) } spin_unlock_irq(&common->lock); + pr_err("%s:cur_state2=%d\n", __func__, old_state); /* Carry out any extra actions required for the exception */ switch (old_state) { case FSG_STATE_ABORT_BULK_OUT: @@ -2502,14 +2811,17 @@ static void handle_exception(struct fsg_common *common) break; case FSG_STATE_CONFIG_CHANGE: + pr_err("%s:status change disable/enable ep\n", __func__); do_set_interface(common, common->new_fsg); if (common->new_fsg) { + pr_err("%s:setup continue call\n", __func__); /* * make sure delayed_status flag updated when set_alt * returned. */ msleep(200); usb_composite_setup_continue(common->cdev); + pr_err("%s:setup continue call done\n", __func__); } break; @@ -2983,6 +3295,7 @@ int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg) pr_info("Number of LUNs=%d\n", cfg->nluns); + pr_err("%s:done\n", __func__); return 0; fail: @@ -3005,6 +3318,9 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, ? "File-CD Gadget" : "File-Stor Gadget"), i); + /*Anderson@, 2016/09/21, CD-ROM and VID customized*/ + snprintf(common->inquiry_string, sizeof(common->inquiry_string), "%s", + "OnePlus Device Driver"); } EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string); @@ -3048,7 +3364,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) unsigned max_burst; int ret; struct fsg_opts *opts; - + pr_err("%s:\n", __func__); /* Don't allow to bind if we don't have at least one LUN */ ret = _fsg_common_get_max_lun(common); if (ret < 0) { @@ -3121,6 +3437,7 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg_ss_function); if (ret) goto autoconf_fail; + pr_err("%s: done\n", __func__); return 0; @@ -3144,6 +3461,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) struct fsg_common *common = fsg->common; DBG(fsg, "unbind\n"); + pr_err("%s:current_state= %d\n", __func__, common->state); if (fsg->common->fsg == fsg) { fsg->common->new_fsg = NULL; raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); @@ -3152,6 +3470,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) } usb_free_all_descriptors(&fsg->function); + pr_err("%s: done\n", __func__); } static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item) @@ -3502,6 +3821,8 @@ static struct usb_function_instance *fsg_alloc_inst(void) memset(&config, 0, sizeof(config)); config.removable = true; + config.cdrom = true; + config.ro = true; rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", (const char **)&opts->func_inst.group.cg_item.ci_name); if (rc) diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index d8cc5fd39e85..2d6f0408a47d 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -39,12 +39,43 @@ #include #include #include +#include #include "configfs.h" #define MTP_RX_BUFFER_INIT_SIZE 1048576 #define MTP_TX_BUFFER_INIT_SIZE 1048576 #define MTP_BULK_BUFFER_SIZE 16384 +/* Currently tx and rx buffer len is 1048576, inter buffer len is 28. + *Tx buffer counts is 8, Rx is 2 and intr is 5. + *In order avoid MTP can't work issue, use fixed memory. + * 0xAC400000(0xffffffc02c400000) ------ + * | TX | 0x100000 * 8 + * 0xACC00000(0xffffffc02cc00000) ------ + * | RX | 0x100000 * 2 + * 0xACE00000(0xffffffc02ce00000) ------ + * | INTR | 0x40(alignment) * 5 + * + *------ + */ +/*Anderson@, 2016/12/09, Add fix memory for MTP*/ +/*0xAC400000, 0xAC500000, 0xAC600000, +*0xAC700000, 0xAC800000, 0xAC900000, +*0xACA00000, 0xACB00000 +*/ + +#define MTP_TX_BUFFER_BASE 0xAC400000 +/*0xACC00000, 0xACD00000*/ +#define MTP_RX_BUFFER_BASE 0xACC00000 +/*0xACE00000, 0xACE00040, 0xACE00080, 0xACE000C0, 0xACE00100*/ +#define MTP_INTR_BUFFER_BASE 0xACE00000 +static int mtpBufferOffset; +static bool useFixAddr; +enum buf_type { + TX_BUFFER = 0, + RX_BUFFER, + INTR_BUFFER, +}; #define INTR_BUFFER_SIZE 28 #define MAX_INST_NAME_LEN 40 #define MTP_MAX_FILE_SIZE 0xFFFFFFFFL @@ -79,6 +110,11 @@ #define DRIVER_NAME "mtp" #define MAX_ITERATION 100 +/* values for qos requests */ +#define FILE_LENGTH (10 * 1024 * 1024) +#define PM_QOS_TIMEOUT 3000000 + +static bool mtp_receive_flag; unsigned int mtp_rx_req_len = MTP_RX_BUFFER_INIT_SIZE; module_param(mtp_rx_req_len, uint, S_IRUGO | S_IWUSR); @@ -90,6 +126,11 @@ unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX; module_param(mtp_tx_reqs, uint, S_IRUGO | S_IWUSR); static const char mtp_shortname[] = DRIVER_NAME "_usb"; +static struct pm_qos_request little_cpu_mtp_freq; +static struct pm_qos_request devfreq_mtp_request; +static struct pm_qos_request big_cpu_mtp_freq; +static struct delayed_work cpu_freq_qos_work; +static struct workqueue_struct *cpu_freq_qos_queue; struct mtp_dev { struct usb_function function; @@ -410,8 +451,9 @@ static inline struct mtp_dev *func_to_mtp(struct usb_function *f) { return container_of(f, struct mtp_dev, function); } - -static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) +/*2016/12/09, Add fix memory for MTP*/ +static struct usb_request *mtp_request_new(struct usb_ep *ep, + int buffer_size, enum buf_type type) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); @@ -419,19 +461,44 @@ static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) return NULL; /* now allocate buffers for the requests */ - req->buf = kmalloc(buffer_size, GFP_KERNEL); + /*2016/12/09, Add fix memory for MTP*/ + if (useFixAddr == true) { + if (type == TX_BUFFER) + req->buf = __va(MTP_TX_BUFFER_BASE + mtpBufferOffset); + else if (type == RX_BUFFER) + req->buf = __va(MTP_RX_BUFFER_BASE + mtpBufferOffset); + else + req->buf = __va(MTP_INTR_BUFFER_BASE + mtpBufferOffset); + } else + req->buf = kmalloc(buffer_size, GFP_KERNEL); + memset(req->buf, 0, buffer_size); + if (!req->buf) { usb_ep_free_request(ep, req); return NULL; } + /*2016/12/09, Add fix memory for MTP*/ + if (useFixAddr == true) { + if (buffer_size == INTR_BUFFER_SIZE) + mtpBufferOffset += 0x40; /*alignment*/ + else + mtpBufferOffset += buffer_size; + } + return req; } static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) { if (req) { - kfree(req->buf); + /*2016/12/09, Add fix memory for MTP*/ + if (useFixAddr == true) { + req->buf = NULL; + mtpBufferOffset = 0; + } else + kfree(req->buf); + usb_ep_free_request(ep, req); } } @@ -555,9 +622,19 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, dev->ep_intr = ep; retry_tx_alloc: + /*2016/12/09, Add fix memory for MTP*/ + if (mtp_tx_req_len == MTP_TX_BUFFER_INIT_SIZE + && mtp_rx_req_len == MTP_RX_BUFFER_INIT_SIZE + && mtp_tx_reqs == MTP_TX_REQ_MAX) + useFixAddr = true; + else + useFixAddr = false; + pr_info("useFixAddr:%s\n", useFixAddr?"true":"false"); + mtpBufferOffset = 0; /* now allocate requests for our endpoints */ for (i = 0; i < mtp_tx_reqs; i++) { - req = mtp_request_new(dev->ep_in, mtp_tx_req_len); + /*2016/12/09, Add fix memory for MTP*/ + req = mtp_request_new(dev->ep_in, mtp_tx_req_len, TX_BUFFER); if (!req) { if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; @@ -581,8 +658,11 @@ retry_tx_alloc: mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; retry_rx_alloc: + /*2016/12/09, Add fix memory for MTP*/ + mtpBufferOffset = 0; for (i = 0; i < RX_REQ_MAX; i++) { - req = mtp_request_new(dev->ep_out, mtp_rx_req_len); + /*2016/12/09, Add fix memory for MTP*/ + req = mtp_request_new(dev->ep_out, mtp_rx_req_len, RX_BUFFER); if (!req) { if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; @@ -594,13 +674,19 @@ retry_rx_alloc: req->complete = mtp_complete_out; dev->rx_req[i] = req; } + /*2016/12/09, Add fix memory for MTP*/ + mtpBufferOffset = 0; for (i = 0; i < INTR_REQ_MAX; i++) { - req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); + /*2016/12/09, Add fix memory for MTP*/ + req = mtp_request_new(dev->ep_intr, + INTR_BUFFER_SIZE, INTR_BUFFER); if (!req) goto fail; req->complete = mtp_complete_intr; mtp_req_put(dev, &dev->intr_idle, req); } + /*2016/12/09, Add fix memory for MTP*/ + mtpBufferOffset = 0; return 0; @@ -825,6 +911,19 @@ static void send_file_work(struct work_struct *data) DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + if (dev->xfer_file_length >= FILE_LENGTH) { + pm_qos_update_request(&devfreq_mtp_request, MAX_CPUFREQ); + pm_qos_update_request(&little_cpu_mtp_freq, MAX_CPUFREQ); + pm_qos_update_request(&big_cpu_mtp_freq, MAX_CPUFREQ - 1); + } else { + pm_qos_update_request_timeout(&devfreq_mtp_request, + MAX_CPUFREQ, PM_QOS_TIMEOUT); + pm_qos_update_request_timeout(&little_cpu_mtp_freq, + MAX_CPUFREQ, PM_QOS_TIMEOUT); + pm_qos_update_request_timeout(&big_cpu_mtp_freq, + MAX_CPUFREQ-1, PM_QOS_TIMEOUT); + } + if (dev->xfer_send_header) { hdr_size = sizeof(struct mtp_data_header); count += hdr_size; @@ -913,6 +1012,12 @@ static void send_file_work(struct work_struct *data) if (req) mtp_req_put(dev, &dev->tx_idle, req); + if (dev->xfer_file_length >= FILE_LENGTH) { + pm_qos_update_request(&devfreq_mtp_request, MIN_CPUFREQ); + pm_qos_update_request(&little_cpu_mtp_freq, MIN_CPUFREQ); + pm_qos_update_request(&big_cpu_mtp_freq, MIN_CPUFREQ); + } + DBG(cdev, "send_file_work returning %d state:%d\n", r, dev->state); /* write the result */ dev->xfer_result = r; @@ -943,6 +1048,11 @@ static void receive_file_work(struct work_struct *data) if (!IS_ALIGNED(count, dev->ep_out->maxpacket)) DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__, count, dev->ep_out->maxpacket); + if (delayed_work_pending(&cpu_freq_qos_work)) + cancel_delayed_work(&cpu_freq_qos_work); + pm_qos_update_request(&devfreq_mtp_request, MAX_CPUFREQ); + pm_qos_update_request(&little_cpu_mtp_freq, MAX_CPUFREQ); + pm_qos_update_request(&big_cpu_mtp_freq, MAX_CPUFREQ - 1); while (count > 0 || write_req) { if (count > 0) { @@ -1047,12 +1157,20 @@ static void receive_file_work(struct work_struct *data) } } + queue_delayed_work(cpu_freq_qos_queue, &cpu_freq_qos_work, msecs_to_jiffies(1000)*3); DBG(cdev, "receive_file_work returning %d\n", r); /* write the result */ dev->xfer_result = r; smp_wmb(); } +static void update_qos_request(struct work_struct *data) +{ + pm_qos_update_request(&devfreq_mtp_request, MIN_CPUFREQ); + pm_qos_update_request(&little_cpu_mtp_freq, MIN_CPUFREQ); + pm_qos_update_request(&big_cpu_mtp_freq, MIN_CPUFREQ); +} + static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) { struct usb_request *req = NULL; @@ -1137,6 +1255,11 @@ static long mtp_send_receive_ioctl(struct file *fp, unsigned code, dev->xfer_send_header = 0; } else { work = &dev->receive_file_work; + pm_qos_update_request(&devfreq_mtp_request, MAX_CPUFREQ); + pm_qos_update_request(&little_cpu_mtp_freq, MAX_CPUFREQ); + pm_qos_update_request(&big_cpu_mtp_freq, MAX_CPUFREQ - 1); + msm_cpuidle_set_sleep_disable(true); + mtp_receive_flag = true; } /* We do the file transfer on a work queue so it will run @@ -1146,6 +1269,16 @@ static long mtp_send_receive_ioctl(struct file *fp, unsigned code, queue_work(dev->wq, work); /* wait for operation to complete */ flush_workqueue(dev->wq); + if (mtp_receive_flag) { + mtp_receive_flag = false; + pm_qos_update_request_timeout(&devfreq_mtp_request, + MAX_CPUFREQ, PM_QOS_TIMEOUT); + pm_qos_update_request_timeout(&little_cpu_mtp_freq, + MAX_CPUFREQ, PM_QOS_TIMEOUT); + pm_qos_update_request_timeout(&big_cpu_mtp_freq, + MAX_CPUFREQ-1, PM_QOS_TIMEOUT); + msm_cpuidle_set_sleep_disable(false); + } fput(filp); /* read the result */ @@ -1292,6 +1425,11 @@ static int mtp_release(struct inode *ip, struct file *fp) { printk(KERN_INFO "mtp_release\n"); + if (mtp_receive_flag) { + mtp_receive_flag = false; + msm_cpuidle_set_sleep_disable(false); + } + mtp_unlock(&_mtp_dev->open_excl); return 0; } @@ -1729,6 +1867,12 @@ static int __mtp_setup(struct mtp_instance *fi_mtp) INIT_WORK(&dev->send_file_work, send_file_work); INIT_WORK(&dev->receive_file_work, receive_file_work); + cpu_freq_qos_queue = create_singlethread_workqueue("f_mtp_qos"); + INIT_DELAYED_WORK(&cpu_freq_qos_work, update_qos_request); + pm_qos_add_request(&devfreq_mtp_request, PM_QOS_DEVFREQ_MIN, MIN_CPUFREQ); + pm_qos_add_request(&little_cpu_mtp_freq, PM_QOS_C0_CPUFREQ_MIN, MIN_CPUFREQ); + pm_qos_add_request(&big_cpu_mtp_freq, PM_QOS_C1_CPUFREQ_MIN, MIN_CPUFREQ); + _mtp_dev = dev; ret = misc_register(&mtp_device); @@ -1739,6 +1883,10 @@ static int __mtp_setup(struct mtp_instance *fi_mtp) return 0; err2: + pm_qos_remove_request(&big_cpu_mtp_freq); + pm_qos_remove_request(&little_cpu_mtp_freq); + pm_qos_remove_request(&devfreq_mtp_request); + destroy_workqueue(cpu_freq_qos_queue); destroy_workqueue(dev->wq); err1: _mtp_dev = NULL; @@ -1761,6 +1909,10 @@ static void mtp_cleanup(void) return; mtp_debugfs_remove(); + pm_qos_remove_request(&big_cpu_mtp_freq); + pm_qos_remove_request(&little_cpu_mtp_freq); + pm_qos_remove_request(&devfreq_mtp_request); + destroy_workqueue(cpu_freq_qos_queue); misc_deregister(&mtp_device); destroy_workqueue(dev->wq); _mtp_dev = NULL; @@ -1879,6 +2031,10 @@ static int mtp_ctrlreq_configfs(struct usb_function *f, static void mtp_free(struct usb_function *f) { /*NO-OP: no function specific resource allocation in mtp_alloc*/ + struct mtp_instance *fi_mtp; + + fi_mtp = container_of(f->fi, struct mtp_instance, func_inst); + fi_mtp->func_inst.f = NULL; } struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index 8e558c364abc..9f8e257de607 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -1151,14 +1151,18 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) void rndis_ipa_reset_trigger(void) { struct f_rndis_qc *rndis; + unsigned long flags; + spin_lock_irqsave(&rndis_lock, flags); rndis = _rndis_qc; if (!rndis) { pr_err("%s: No RNDIS instance", __func__); + spin_unlock_irqrestore(&rndis_lock, flags); return; } rndis->net_ready_trigger = false; + spin_unlock_irqrestore(&rndis_lock, flags); } /* @@ -1527,6 +1531,12 @@ static struct usb_function_instance *qcrndis_alloc_inst(void) return &opts->func_inst; } +static void rndis_qc_cleanup(void) +{ + pr_debug("rndis QC cleanup\n"); + misc_deregister(&rndis_qc_device); +} + void *rndis_qc_get_ipa_rx_cb(void) { return rndis_ipa_params.ipa_rx_notify; @@ -1564,6 +1574,7 @@ static int __init usb_qcrndis_init(void) static void __exit usb_qcrndis_exit(void) { usb_function_unregister(&rndis_bamusb_func); + rndis_qc_cleanup(); } module_init(usb_qcrndis_init); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a6f886e5b2f3..049d8388f1b1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -25,9 +25,12 @@ #include #include #include - +#include #include "xhci.h" #include "xhci-trace.h" +static bool usb2_lpm_disable = 1; +module_param(usb2_lpm_disable, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(usb2_lpm_disable, "DISABLE USB2 LPM"); /* * Allocates a generic ring segment from the ring pool, sets the dma address, @@ -2306,9 +2309,10 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHCI 1.0: support USB2 software lpm"); xhci->sw_lpm_support = 1; - if (temp & XHCI_HLC) { + if (!usb2_lpm_disable && (temp & XHCI_HLC)) { xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHCI 1.0: support USB2 hardware lpm"); + xhci_err(xhci, "xHCI 1.0: support USB2 hardware lpm"); xhci->hw_lpm_support = 1; } } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5bfe47816ac3..cc8eb932f347 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2744,6 +2744,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) if (status == 0xffffffff) goto hw_died; + if (status & STS_HCE) { + xhci_warn(xhci, "WARNING: Host controller Error\n"); + } + if (!(status & STS_EINT)) { spin_unlock(&xhci->lock); return IRQ_NONE; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 91af817278e3..e5565dc61a8d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -115,8 +115,10 @@ int xhci_halt(struct xhci_hcd *xhci) if (!ret) { xhci->xhc_state |= XHCI_STATE_HALTED; } else { - xhci_warn(xhci, "Host not halted after %u microseconds.\n", - XHCI_MAX_HALT_USEC); + + /*2016/07/01, If host controller is not halted, otg can't work*/ + xhci_warn(xhci, "Host not halted after %u microseconds. ret:%d\n", + XHCI_MAX_HALT_USEC, ret); } xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; @@ -995,6 +997,19 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; } + if ((readl_relaxed(&xhci->op_regs->status) & STS_EINT) || + (readl_relaxed(&xhci->op_regs->status) & STS_PORT)) { + xhci_warn(xhci, "WARN: xHC EINT/PCD set status:%x\n", + readl_relaxed(&xhci->op_regs->status)); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + /* step 4: set Run/Stop bit */ + command = readl_relaxed(&xhci->op_regs->command); + command |= CMD_RUN; + writel_relaxed(command, &xhci->op_regs->command); + spin_unlock_irq(&xhci->lock); + return -EBUSY; + } xhci_clear_command_ring(xhci); /* step 3: save registers */ @@ -2857,6 +2872,12 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg_ctx(xhci, virt_dev->in_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); + /*2016/07/01, If host controller is not halted, otg can't work*/ + if (hcd->state == HC_STATE_QUIESCING) { + xhci_warn(xhci, "hcd->state:%d\n", hcd->state); + goto command_cleanup; + } + ret = xhci_configure_endpoint(xhci, udev, command, false, false); if (ret) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index a2cb7147a8ee..2e60ef907b27 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -748,6 +748,8 @@ static void kick_sm(struct usbpd *pd, int ms) static void phy_sig_received(struct usbpd *pd, enum pd_sig_type sig) { union power_supply_propval val = {1}; + usbpd_info(&pd->dev, "%s return by oem\n", __func__); + return; if (sig != HARD_RESET_SIG) { usbpd_err(&pd->dev, "invalid signal (%d) received\n", sig); @@ -1052,7 +1054,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) */ } - dual_role_instance_changed(pd->dual_role); /* Set CC back to DRP toggle for the next disconnect */ val.intval = POWER_SUPPLY_TYPEC_PR_DUAL; @@ -1214,11 +1215,12 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) if (pd->psy_type == POWER_SUPPLY_TYPE_USB || pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP || pd->psy_type == POWER_SUPPLY_TYPE_USB_FLOAT || - usb_compliance_mode) + usb_compliance_mode){ + usbpd_err(&pd->dev, "sink start:pd start peripheral\n"); start_usb_peripheral(pd); + } } - dual_role_instance_changed(pd->dual_role); ret = power_supply_get_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ALLOWED, &val); @@ -1328,6 +1330,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_TRANSITION_TO_DEFAULT: if (pd->current_dr != DR_UFP) { + usbpd_err(&pd->dev, "to default:pd start peripheral\n"); stop_usb_host(pd); start_usb_peripheral(pd); pd->current_dr = DR_UFP; @@ -1829,21 +1832,6 @@ enable_reg: else pd->vbus_enabled = true; - count = 10; - /* - * Check to make sure VBUS voltage reaches above Vsafe5Vmin (4.75v) - * before proceeding. - */ - while (count--) { - ret = power_supply_get_property(pd->usb_psy, - POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); - if (ret || val.intval >= 4750000) /*vsafe5Vmin*/ - break; - usleep_range(10000, 12000); /* Delay between two reads */ - } - - if (ret) - msleep(100); /* Delay to wait for VBUS ramp up if read fails */ return ret; } @@ -2056,41 +2044,23 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_SEND_CAPABILITIES: - ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, - ARRAY_SIZE(default_src_caps), SOP_MSG); + ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG); if (ret) { pd->caps_count++; - if (pd->caps_count == 10 && pd->current_dr == DR_DFP) { - /* Likely not PD-capable, start host now */ + if (pd->caps_count < 10 && pd->current_dr == DR_DFP) { start_usb_host(pd, true); - } else if (pd->caps_count >= PD_CAPS_COUNT) { - usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n"); + } else if (pd->caps_count >= 10) { usbpd_set_state(pd, PE_SRC_DISABLED); - - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, - &val); break; } - kick_sm(pd, SRC_CAP_TIME); break; } - /* transmit was successful if GoodCRC was received */ - pd->caps_count = 0; - pd->hard_reset_count = 0; - pd->pd_connected = true; /* we know peer is PD capable */ - - /* wait for REQUEST */ - pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT; - kick_sm(pd, SENDER_RESPONSE_TIME); - - val.intval = 1; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_ACTIVE, &val); + usbpd_info(&pd->dev, "Start host snd msg ok\n"); + if (pd->current_dr == DR_DFP) + start_usb_host(pd, true); break; case PE_SRC_SEND_CAPABILITIES_WAIT: @@ -2620,6 +2590,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_TRANSITION_TO_DEFAULT: + usbpd_err(&pd->dev, "default: sink startup\n"); usbpd_set_state(pd, PE_SNK_STARTUP); break; @@ -2900,9 +2871,18 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) if (pd->typec_mode == typec_mode) return 0; + if ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || + (typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM) || + (typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH)) { + if (pd->psy_type == POWER_SUPPLY_TYPE_UNKNOWN) { + usbpd_err(&pd->dev, "typec_mode:%d, psy_type:%d\n", + typec_mode, pd->psy_type); + return 0; + } + } pd->typec_mode = typec_mode; - usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", + usbpd_err(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n", typec_mode, pd->vbus_present, pd->psy_type, usbpd_get_plug_orientation(pd)); diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c index e5f38e42e165..c650ac47baf6 100644 --- a/drivers/usb/phy/phy-msm-qusb-v2.c +++ b/drivers/usb/phy/phy-msm-qusb-v2.c @@ -50,6 +50,11 @@ #define DPSE_INTERRUPT BIT(0) #define QUSB2PHY_PORT_TUNE1 0x23c +#define QUSB2PHY_PORT_TUNE2 0x240 +#define QUSB2PHY_PORT_TUNE3 0x244 +#define QUSB2PHY_PORT_TUNE4 0x248 +#define QUSB2PHY_IMP_CTRL1 0x21c +#define QUSB2PHY_IMP_CTRL2 0x220 #define QUSB2PHY_TEST1 0x24C #define QUSB2PHY_1P2_VOL_MIN 1200000 /* uV */ @@ -73,6 +78,21 @@ unsigned int phy_tune1; module_param(phy_tune1, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1"); +unsigned int phy_tune2; +module_param(phy_tune2, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune2, "QUSB PHY v2 TUNE2"); +unsigned int phy_tune3; +module_param(phy_tune3, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune3, "QUSB PHY v2 TUNE3"); +unsigned int phy_tune4; +module_param(phy_tune4, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(phy_tune4, "QUSB PHY v2 TUNE4"); +unsigned int imp_ctrl1; +module_param(imp_ctrl1, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(imp_ctrl1, "QUSB PHY v2 IMP_CTRL1"); +unsigned int imp_ctrl2; +module_param(imp_ctrl2, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(imp_ctrl2, "QUSB PHY v2 IMP_CTRL2"); struct qusb_phy { struct usb_phy phy; @@ -530,11 +550,46 @@ static int qusb_phy_init(struct usb_phy *phy) /* If phy_tune1 modparam set, override tune1 value */ if (phy_tune1) { - pr_debug("%s(): (modparam) TUNE1 val:0x%02x\n", + pr_err("%s(): (modparam) TUNE1 val:0x%02x\n", __func__, phy_tune1); writel_relaxed(phy_tune1, qphy->base + QUSB2PHY_PORT_TUNE1); } + /* If phy_tune2 modparam set, override tune2 value */ + if (phy_tune2) { + pr_err("%s(): (modparam) TUNE2 val:0x%02x\n", + __func__, phy_tune2); + writel_relaxed(phy_tune2, + qphy->base + QUSB2PHY_PORT_TUNE2); + } + /* If phy_tune3 modparam set, override tune3 value */ + if (phy_tune3) { + pr_err("%s(): (modparam) TUNE3 val:0x%02x\n", + __func__, phy_tune3); + writel_relaxed(phy_tune3, + qphy->base + QUSB2PHY_PORT_TUNE3); + } + /* If phy_tune4 modparam set, override tune4 value */ + if (phy_tune4) { + pr_err("%s(): (modparam) TUNE4 val:0x%02x\n", + __func__, phy_tune4); + writel_relaxed(phy_tune4, + qphy->base + QUSB2PHY_PORT_TUNE4); + } + /* If imp_ctrl1 modparam set, override imp_ctrl1 value */ + if (imp_ctrl1) { + pr_err("%s(): (modparam) IMP_CTRL1 val:0x%02x\n", + __func__, imp_ctrl1); + writel_relaxed(imp_ctrl1, + qphy->base + QUSB2PHY_IMP_CTRL1); + } + /* If imp_ctrl2 modparam set, override imp_ctrl2 value */ + if (imp_ctrl2) { + pr_err("%s(): (modparam) IMP_CTRL2 val:0x%02x\n", + __func__, imp_ctrl2); + writel_relaxed(imp_ctrl2, + qphy->base + QUSB2PHY_IMP_CTRL2); + } /* ensure above writes are completed before re-enabling PHY */ wmb(); diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 2a708dd2ecee..376c5824d48a 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -32,9 +32,20 @@ #include #include #include +#include +#include +#include +#include #include +#define LCDSPEEDUP_LITTLE_CPU_QOS_FREQ 1900800 +#define LCDSPEEDUP_BIG_CPU_QOS_FREQ 2361600 +#define LCD_QOS_TIMEOUT 250000 +#define NO_BOOST 0 + +static struct pm_qos_request lcdspeedup_little_cpu_qos; +static struct pm_qos_request lcdspeedup_big_cpu_qos; /* * Frame buffer device initialization and setup routines @@ -1919,3 +1930,78 @@ int fb_new_modelist(struct fb_info *info) } MODULE_LICENSE("GPL"); + +static int fb_state_change(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fb_event *evdata = data; + struct fb_info *info = evdata->info; + unsigned int blank; + + if (val != FB_EVENT_BLANK && + val != FB_EARLY_EVENT_BLANK) + return NOTIFY_OK; + + if (info->node) + return NOTIFY_OK; + + blank = *(int *)evdata->data; + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (val == FB_EARLY_EVENT_BLANK) { + pm_qos_update_request(&lcdspeedup_little_cpu_qos, MIN_CPUFREQ); + pm_qos_update_request(&lcdspeedup_big_cpu_qos, MIN_CPUFREQ); + /* add print actvie ws */ + pm_print_active_wakeup_sources_queue(true); + pr_debug("::: LCD start off :::\n"); + } + break; + case FB_BLANK_UNBLANK: + if (val == FB_EARLY_EVENT_BLANK) { + struct cpufreq_policy *policy; + /* Speed up LCD on */ + /* Fetch little cpu policy and drive the CPU towards target frequency */ + pm_qos_update_request_timeout( + &lcdspeedup_little_cpu_qos, MAX_CPUFREQ, + LCD_QOS_TIMEOUT); + + /* Fetch big cpu policy and drive big cpu towards target frequency */ + policy = cpufreq_cpu_get(cluster1_first_cpu); + if (policy) { + cpufreq_driver_target(policy, LCDSPEEDUP_BIG_CPU_QOS_FREQ, CPUFREQ_RELATION_H); + pm_qos_update_request_timeout(&lcdspeedup_big_cpu_qos, (MAX_CPUFREQ-4), LCD_QOS_TIMEOUT); + } else + return NOTIFY_OK; + cpufreq_cpu_put(policy); + } + + if (val == FB_EVENT_BLANK) { + //Wujialong 20160314 enable sched_boost when wakeup and disable sched_boost when screen on + sched_set_boost(NO_BOOST); + /* remove print actvie ws */ + pm_print_active_wakeup_sources_queue(false); + pr_debug("::: LCD is on :::\n"); + } + break; + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block fb_block = { + .notifier_call = fb_state_change, + .priority = 1, +}; + +static int __init lcdscreen_speedup_init_pm_qos(void) +{ + fb_register_client(&fb_block); + pm_qos_add_request(&lcdspeedup_little_cpu_qos, PM_QOS_C0_CPUFREQ_MIN, MIN_CPUFREQ); + pm_qos_add_request(&lcdspeedup_big_cpu_qos, PM_QOS_C1_CPUFREQ_MIN, MIN_CPUFREQ); + + return 0; +} +late_initcall(lcdscreen_speedup_init_pm_qos); diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index 03ee89ad0d99..2d2a31b35577 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -122,4 +122,53 @@ config FB_MSM_MDSS_XLOG_DEBUG driver during fatal errors and enable some display-driver logging into an internal buffer (this avoids logging overhead). + +config IRIS2_IO + tristate "IRIS2 IO Driver" + default y + ---help--- + Say y to enable + +config BACKLIGHT_LP8557E01 + tristate "LP8557E01 Backlight Driver" + default y + ---help--- + Say y to enable + +config IRIS2_FULL_SUPPORT + bool "Enable IRIS2 full support" + default n + ---help--- + Say y to enable + +config IRIS2_LIGHTUP_ONLY + bool "Lightup panel only (IRIS2)" + default n + ---help--- + Say y to enable + +config IRIS2_DRC_SUPPORT + bool "Enable IRIS2 DRC Support" + default n + ---help--- + Say y to enable + +config IRIS2P_FULL_SUPPORT + bool "Enable IRIS2P Support" + default n + ---help--- + Say y to enable + +config IRIS2P_LIGHTUP_ONLY + bool "Lightup panel only (IRIS2P)" + default n + ---help--- + Say y to enable + +config IRIS2P_DRC_SUPPORT + bool "Enable IRIS2P DRC support" + default n + ---help--- + Say y to enable + endif diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index e101b873f361..648372727b3b 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -37,6 +37,10 @@ mdss-dsi-objs += msm_mdss_io_8974.o mdss-dsi-objs += mdss_dsi_phy.o mdss-dsi-objs += mdss_dsi_phy_v3.o mdss-dsi-objs += mdss_dsi_clk.o +ifeq ($(CONFIG_IRIS2P_FULL_SUPPORT),y) +mdss-dsi-objs += mdss_dsi_iris2p_lightup.o mdss_dsi_iris2p_def.o +endif + obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_hdmi_util.o diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c index 10d747962a91..4d87c05cc9e5 100644 --- a/drivers/video/fbdev/msm/mdss_debug_xlog.c +++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c @@ -23,12 +23,12 @@ #include "mdss_debug.h" #ifdef CONFIG_FB_MSM_MDSS_XLOG_DEBUG -#define XLOG_DEFAULT_ENABLE 1 +#define XLOG_DEFAULT_ENABLE 0 #else #define XLOG_DEFAULT_ENABLE 0 #endif -#define XLOG_DEFAULT_PANIC 1 +#define XLOG_DEFAULT_PANIC 0 #define XLOG_DEFAULT_REGDUMP 0x2 /* dump in RAM */ #define XLOG_DEFAULT_DBGBUSDUMP 0x2 /* dump in RAM */ #define XLOG_DEFAULT_VBIF_DBGBUSDUMP 0x2 /* dump in RAM */ diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 0d41f41371dd..cf34f3a9b7e1 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -393,6 +393,10 @@ static int mdss_dsi_panel_power_off(struct mdss_panel_data *pdata) pr_err("%s: failed to disable vregs for %s\n", __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); + /*POC and vci voltage disable*/ + mdss_dsi_disp_poc_en(pdata, 0); + mdss_dsi_disp_vci_en(pdata, 0); + end: return ret; } @@ -410,6 +414,10 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata) ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + + /* POC voltage enable */ + mdss_dsi_disp_poc_en(pdata, 1); + ret = msm_dss_enable_vreg( ctrl_pdata->panel_power_data.vreg_config, ctrl_pdata->panel_power_data.num_vreg, 1); @@ -418,7 +426,8 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata) __func__, __mdss_dsi_pm_name(DSI_PANEL_PM)); return ret; } - + + mdss_dsi_disp_vci_en(pdata, 1); /* * If continuous splash screen feature is enabled, then we need to * request all the GPIOs that have already been configured in the @@ -451,6 +460,7 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, int ret = 0; struct mdss_panel_info *pinfo; + if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; @@ -1133,7 +1143,6 @@ static int _mdss_dsi_refresh_cmd(struct buf_data *new_cmds, if (new_cmds->sync_flag) return 0; - bp = new_cmds->buf; len = new_cmds->blen; cnt = 0; @@ -1155,7 +1164,6 @@ static int _mdss_dsi_refresh_cmd(struct buf_data *new_cmds, bp[0], len); return -EINVAL; } - /* Reallocate space for dcs commands */ cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc), GFP_KERNEL); if (!cmds) { @@ -1301,7 +1309,8 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata, int power_state) pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } - + pr_err("%s start\n", __func__); +//#endif ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); @@ -1363,7 +1372,8 @@ panel_power_ctrl: ctrl_pdata->cur_max_pkt_size = 0; end: pr_debug("%s-:\n", __func__); - + pr_err("%s end\n", __func__); +//#endif return ret; } @@ -1484,7 +1494,8 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) pr_err("%s: Invalid input data\n", __func__); return -EINVAL; } - + pr_err("%s start\n", __func__); +//#endif ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); @@ -1575,6 +1586,8 @@ int mdss_dsi_on(struct mdss_panel_data *pdata) end: pr_debug("%s-:\n", __func__); + pr_err("%s end\n", __func__); +//#endif return ret; } @@ -1693,12 +1706,26 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata) ATRACE_END("dsi_panel_on"); } } + if (!ctrl_pdata->setting_mode_loaded) { + ctrl_pdata->setting_mode_loaded = true; + mutex_lock(&ctrl_pdata->panel_mode_lock); + ctrl_pdata->is_panel_on = true; + mutex_unlock(&ctrl_pdata->panel_mode_lock); + } if ((pdata->panel_info.type == MIPI_CMD_PANEL) && mipi->vsync_enable && mipi->hw_vsync_mode) { mdss_dsi_set_tear_on(ctrl_pdata); + + /*delayed_work*/ if (mdss_dsi_is_te_based_esd(ctrl_pdata)) - enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + schedule_delayed_work(&ctrl_pdata->techeck_work, + msecs_to_jiffies(3000)); + /* #else */ + /* if (mdss_dsi_is_te_based_esd(ctrl_pdata)) + * enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); + */ + } ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT; @@ -1769,8 +1796,13 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) if ((pdata->panel_info.type == MIPI_CMD_PANEL) && mipi->vsync_enable && mipi->hw_vsync_mode) { if (mdss_dsi_is_te_based_esd(ctrl_pdata)) { - disable_irq(gpio_to_irq( - ctrl_pdata->disp_te_gpio)); + + /* #ifdef VENDOR */ + cancel_delayed_work_sync(&ctrl_pdata->techeck_work); + /* disable_irq(gpio_to_irq( + * ctrl_pdata->disp_te_gpio)); + */ + /* #endif */ atomic_dec(&ctrl_pdata->te_irq_ready); } mdss_dsi_set_tear_off(ctrl_pdata); @@ -2562,6 +2594,8 @@ static int mdss_dsi_reset_write_ptr(struct mdss_panel_data *pdata) struct mdss_panel_info *pinfo; int rc = 0; + + if (pdata == NULL) { pr_err("%s: Invalid input data\n", __func__); return -EINVAL; @@ -2850,6 +2884,63 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, &ctrl_pdata->dba_work, HZ); } break; + case MDSS_EVENT_PANEL_SET_SRGB_MODE: + ctrl_pdata->SRGB_mode = (int)(unsigned long) arg; + if (ctrl_pdata->SRGB_mode == 1) + ctrl_pdata->dci_p3_mode = 0; + mdss_dsi_panel_set_srgb_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->SRGB_mode); + break; + case MDSS_EVENT_PANEL_GET_SRGB_MODE: + rc = mdss_dsi_panel_get_srgb_mode(ctrl_pdata); + break; + + case MDSS_EVENT_PANEL_SET_ADOBE_RGB_MODE: + ctrl_pdata->Adobe_RGB_mode = (int)(unsigned long) arg; + mdss_dsi_panel_set_adobe_rgb_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->Adobe_RGB_mode); + break; + case MDSS_EVENT_PANEL_GET_ADOBE_RGB_MODE: + rc = mdss_dsi_panel_get_adobe_rgb_mode(ctrl_pdata); + break; + /* #endif */ + case MDSS_EVENT_PANEL_SET_DCI_P3_MODE: + ctrl_pdata->dci_p3_mode = (int)(unsigned long) arg; + if (ctrl_pdata->dci_p3_mode == 1) + ctrl_pdata->SRGB_mode = 0; + mdss_dsi_panel_set_dci_p3_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->dci_p3_mode); + break; + case MDSS_EVENT_PANEL_GET_DCI_P3_MODE: + rc = mdss_dsi_panel_get_dci_p3_mode(ctrl_pdata); + break; + /* #endif */ + case MDSS_EVENT_PANEL_SET_NIGHT_MODE: + ctrl_pdata->night_mode = (int)(unsigned long) arg; + mdss_dsi_panel_set_night_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->night_mode); + break; + case MDSS_EVENT_PANEL_GET_NIGHT_MODE: + rc = mdss_dsi_panel_get_night_mode(ctrl_pdata); + break; + case MDSS_EVENT_PANEL_SET_ONEPLUS_MODE: + ctrl_pdata->oneplus_mode = (int)(unsigned long) arg; + mdss_dsi_panel_set_oneplus_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->oneplus_mode); + break; + case MDSS_EVENT_PANEL_GET_ONEPLUS_MODE: + rc = mdss_dsi_panel_get_oneplus_mode(ctrl_pdata); + break; + + case MDSS_EVENT_PANEL_SET_ADAPTION_MODE: + ctrl_pdata->adaption_mode = (int)(unsigned long) arg; + mdss_dsi_panel_set_adaption_mode(ctrl_pdata, + (int)(unsigned long) ctrl_pdata->adaption_mode); + break; + case MDSS_EVENT_PANEL_GET_ADAPTION_MODE: + rc = mdss_dsi_panel_get_adaption_mode(ctrl_pdata); + break; + /* #endif */ case MDSS_EVENT_DSI_TIMING_DB_CTRL: mdss_dsi_timing_db_ctrl(ctrl_pdata, (int)(unsigned long)arg); break; @@ -3290,6 +3381,35 @@ error: return rc; } + +static void techeck_work_func(struct work_struct *work) +{ + int ret = 0; + int irq = 0; + struct mdss_dsi_ctrl_pdata *pdata = NULL; + + pdata = container_of(to_delayed_work(work), + struct mdss_dsi_ctrl_pdata, techeck_work); + if (gpio_is_valid(pdata->disp_te_gpio)) + irq = gpio_to_irq(pdata->disp_te_gpio); + else + return; + pdata->te_comp.done = 0; + enable_irq(irq); + ret = wait_for_completion_killable_timeout(&pdata->te_comp, + msecs_to_jiffies(300)); + if (!atomic_read(&pdata->te_irq_ready)) + atomic_inc(&pdata->te_irq_ready); + if (ret == 0) { + disable_irq(irq); + return; + } + disable_irq(irq); + schedule_delayed_work(&pdata->techeck_work, msecs_to_jiffies(3000)); +} + + + static int mdss_dsi_ctrl_probe(struct platform_device *pdev) { int rc = 0; @@ -3414,6 +3534,13 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) return rc; } + if (mdss_dsi_is_te_based_esd(ctrl_pdata)) { + init_completion(&ctrl_pdata->te_comp); + INIT_DELAYED_WORK(&ctrl_pdata->techeck_work, + techeck_work_func); + } + /* #endif */ + if (mdss_dsi_is_te_based_esd(ctrl_pdata)) { rc = devm_request_irq(&pdev->dev, gpio_to_irq(ctrl_pdata->disp_te_gpio), @@ -4314,7 +4441,57 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, pr_debug("%s:%d mode gpio not specified\n", __func__, __LINE__); ctrl_pdata->lcd_mode_sel_gpio = -EINVAL; } + ctrl_pdata->disp_vci_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-vci-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->disp_vci_en_gpio)) + pr_err("%s:%d, vci gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->disp_poc_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-poc-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->disp_poc_en_gpio)) + pr_err("%s:%d, poc gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->isp_1v1_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,isp-1v1-en-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->isp_1v1_en_gpio)) + pr_err("%s:%d, isp-1v1-en gpio not specified\n", + __func__, __LINE__); + + ctrl_pdata->px_bp_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, "qcom,px-bp-gpio", 0); + if (gpio_is_valid(ctrl_pdata->px_bp_gpio)){ + if (gpio_request(ctrl_pdata->px_bp_gpio, "px-bp-gpio")){ + pr_err("%s:%d, px_bp_gpio request fail.\n", + __func__, __LINE__); + }else + gpio_direction_output(ctrl_pdata->px_bp_gpio, 0); + }else{ + pr_err("%s:%d, px_bp_gpio gpio not specified\n", __func__, __LINE__); + } + + if (!of_property_read_string(ctrl_pdev->dev.of_node, "qcom,px-ext-clk", &ctrl_pdata->px_clk_src_name)){ + if (!strcmp(ctrl_pdata->px_clk_src_name, "BBCLK2")) { + ctrl_pdata->px_clk_src = clk_get(&ctrl_pdev->dev, "px_ext_clk"); + if(IS_ERR(ctrl_pdata->px_clk_src)) { + pr_err("can not get px_ext_clk\n"); + }else{ + clk_set_rate(ctrl_pdata->px_clk_src, 19200000); + if (clk_prepare_enable(ctrl_pdata->px_clk_src)){ + pr_err("Enable px_clk fail!\n"); + ctrl_pdata->px_clk_enabled = 0; + }else{ + ctrl_pdata->px_clk_enabled = 1; + } + } + } + }else{ + pr_err("%s:%d, can not get px_clk_src_name.\n", + __func__, __LINE__); + } + +/* #endif */ return 0; } diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 9847016fed29..8bd8a140773e 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -446,6 +446,8 @@ struct mdss_dsi_ctrl_pdata { struct clk *pixel_clk_rcg; struct clk *vco_dummy_clk; struct clk *byte_intf_clk; + struct mutex panel_mode_lock; + //#endif u8 ctrl_state; int panel_mode; int irq_cnt; @@ -521,6 +523,9 @@ struct mdss_dsi_ctrl_pdata { struct completion bta_comp; spinlock_t irq_lock; spinlock_t mdp_lock; +#if defined(CONFIG_IRIS2_FULL_SUPPORT) || defined(CONFIG_IRIS2P_FULL_SUPPORT) + spinlock_t iris_lock; +#endif int mdp_busy; struct mutex mutex; struct mutex cmd_mutex; @@ -574,9 +579,53 @@ struct mdss_dsi_ctrl_pdata { int m_mdp_vote_cnt; /* debugfs structure */ struct mdss_dsi_debugfs_info *debugfs_info; - struct dsi_err_container err_cont; + struct delayed_work techeck_work; + struct completion te_comp; + + int disp_vci_en_gpio; + int disp_poc_en_gpio; + + + bool is_panel_on; + bool setting_mode_loaded; + + int SRGB_mode; + struct dsi_panel_cmds srgb_on_cmds; + struct dsi_panel_cmds srgb_off_cmds; + + + int Adobe_RGB_mode; + struct dsi_panel_cmds Adobe_RGB_on_cmds; + struct dsi_panel_cmds Adobe_RGB_off_cmds; + + int dci_p3_mode; + struct dsi_panel_cmds dci_p3_on_cmds; + struct dsi_panel_cmds dci_p3_off_cmds; + + int night_mode; + struct dsi_panel_cmds night_mode_on_cmds; + struct dsi_panel_cmds night_mode_off_cmds; + int oneplus_mode; + struct dsi_panel_cmds oneplus_mode_on_cmds; + struct dsi_panel_cmds oneplus_mode_off_cmds; + int adaption_mode; + struct dsi_panel_cmds adaption_mode_on_cmds; + struct dsi_panel_cmds adaption_mode_off_cmds; + + + int px_clk_req_gpio; + int px_clk_clk_en_gpio; + const char *px_clk_src_name; + struct clk *px_clk_src; + int px_clk_enabled; + int px_bp_gpio; + int isp_1v1_en_gpio; + bool bl_high2bit; + bool high_brightness_panel; + + struct kobject *kobj; int fb_node; @@ -599,6 +648,44 @@ struct dsi_status_data { struct msm_fb_data_type *mfd; }; +int mdss_dsi_panel_set_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_panel_set_adobe_rgb_mode + (struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_adobe_rgb_mode + (struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_panel_set_dci_p3_mode + (struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_dci_p3_mode(struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_panel_set_night_mode + (struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_night_mode + (struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_panel_set_oneplus_mode + (struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_oneplus_mode(struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_panel_set_adaption_mode + (struct mdss_dsi_ctrl_pdata *ctrl, int level); +int mdss_dsi_panel_get_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl); + + +int mdss_dsi_disp_poc_en(struct mdss_panel_data *pdata, int enable); +int mdss_dsi_px_clk_req(struct mdss_panel_data *pdata, int enable); +int mdss_dsi_disp_vci_en(struct mdss_panel_data *pdata, int enable); +int mdss_dsi_isp_1v1_en(struct mdss_panel_data *pdata, int enable); + +/* #endif */ + void mdss_dsi_read_hw_revision(struct mdss_dsi_ctrl_pdata *ctrl); int dsi_panel_device_register(struct platform_device *ctrl_pdev, struct device_node *pan_node, struct mdss_dsi_ctrl_pdata *ctrl_pdata); diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index 34cedaaa5864..64d783d20554 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -29,6 +29,9 @@ #include "mdss_debug.h" #include "mdss_smmu.h" #include "mdss_dsi_phy.h" +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) +#include "mdss_dsi_iris2p_lightup_priv.h" +#endif #define VSYNC_PERIOD 17 #define DMA_TX_TIMEOUT 200 @@ -123,7 +126,12 @@ void mdss_dsi_ctrl_init(struct device *ctrl_dev, mutex_init(&ctrl->cmd_mutex); mutex_init(&ctrl->clk_lane_mutex); mutex_init(&ctrl->cmdlist_mutex); +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) + spin_lock_init(&ctrl->iris_lock); + mdss_dsi_buf_alloc(ctrl_dev, &ctrl->tx_buf, DSI_DMA_TX_BUF_SIZE); +#else mdss_dsi_buf_alloc(ctrl_dev, &ctrl->tx_buf, SZ_4K); +#endif mdss_dsi_buf_alloc(ctrl_dev, &ctrl->rx_buf, SZ_4K); mdss_dsi_buf_alloc(ctrl_dev, &ctrl->status_buf, SZ_4K); ctrl->cmdlist_commit = mdss_dsi_cmdlist_commit; diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p.c new file mode 100644 index 000000000000..3f8517554670 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p.c @@ -0,0 +1,2382 @@ +/* Copyright (c) 2013, Pixelworks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_fb.h" +#include "mdss_dsi.h" +#include "mdss_dsi_iris2p.h" +#include "mdss_i2c_iris.h" +#include "mdss_debug.h" +#include "mdss_dsi_iris2p_def.h" +#include "mdss_dsi_iris2p_extern.h" +#include "mdss_dsi_iris2p_dbg.h" +//#define DSI_VIDEO_BASE 0xE0000 + +#define IRIS_REGS 80 +#define IRIS_RFB_DATA_PATH_DEFAULT 0x0c840000 // TODO: same as appcode +#define IRIS_PT_DATA_PATH_DEFAULT 0x0c840000 // TODO: same as appcode +#define IRIS_FRC_DATA_PATH_DEFAULT 0x0c840000 // TODO: use_efifo_en = 0 + +struct iris_reg_t { + u32 addr; + u32 val; +}; + +struct iris_mgmt_t { + struct work_struct iris_worker; + struct workqueue_struct *iris_wq; + void (*iris_handler)(void); + bool fbo_enable; + bool sbs_enable; + struct msm_fb_data_type *mfd; +}; + + + +/* Activate Delay 0, FBO Enable: 1, Display Mode: FRC Enable, +* PSR Command: PSR update, Capture Enable: Video +*/ +static char fbo_update[2] = {0x15, 0x02}; + +static char imeta[META_PKT_SIZE] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x3), + 0x00, + 0x00, + PWIL_U16(0x2), +}; + +static struct dsi_cmd_desc iris_meta_pkts[] = { + {{DTYPE_GEN_LWRITE, 0, 0, 0, 0, sizeof(imeta)}, imeta}, + {{ DTYPE_GEN_WRITE2, 0, 0, 0, 0, sizeof(fbo_update) }, fbo_update }, +}; + +static struct iris_reg_t iris_regs[IRIS_REGS]; +static int iris_reg_cnt; + +static struct iris_mgmt_t iris_mgmt; + + +//if it use debug info should open DEBUG, or not DEBUG info +//#define DEBUG + + +static int iris_set_ratio(struct iris_config *iris_cfg); + +static int iris_regs_meta_build(void); + +void mdss_dsi_panel_cmds_send_ex(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + /*please pay attention to call this funtion, it only used to send write cmd when panel on/off*/ + pinfo = &(ctrl->panel_data.panel_info); + /* TODO: + Comment below code for partial update, no impact current system. + If enable dcs_cmd_by_left, the Iris + panel can't light up. + Need to debug later. + */ + /* + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + */ + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + if (pcmds->link_state == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +void iris_update_configure(void) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + struct quality_setting * pqlt_def_setting = & iris_info.setting_info.quality_def; + struct quality_setting * pqlt_cur_setting = & iris_info.setting_info.quality_cur; + + pr_debug("iris_update_configure enter\n"); + + mutex_lock(&iris_cfg->config_mutex); + /*pq settings*/ + if (pqlt_def_setting->pq_setting.memcdemo != pqlt_cur_setting->pq_setting.memcdemo) { + pr_debug("memc demo update\n"); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.peakingdemo != pqlt_cur_setting->pq_setting.peakingdemo) { + pr_debug("peaking demo update\n"); + iris_info.update.pq_setting = true; + }else if (pqlt_def_setting->pq_setting.peaking != pqlt_cur_setting->pq_setting.peaking) { + pr_debug("peaking update cur %i def %i\n", pqlt_cur_setting->pq_setting.peaking, pqlt_def_setting->pq_setting.peaking); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.sharpness != pqlt_cur_setting->pq_setting.sharpness) { + pr_debug("sharpness update\n"); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.gamma != pqlt_cur_setting->pq_setting.gamma) { + pr_debug("gamma level update\n"); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.memclevel != pqlt_cur_setting->pq_setting.memclevel) { + pr_debug("memc level update\n"); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.contrast != pqlt_cur_setting->pq_setting.contrast) { + pr_debug("contrast update\n"); + iris_info.update.pq_setting = true; + } else if (pqlt_def_setting->pq_setting.cinema_en != pqlt_cur_setting->pq_setting.cinema_en) { + pr_debug("memc cinema update\n"); + iris_info.update.pq_setting = true; + } + + if (iris_info.update.pq_setting) { + pqlt_cur_setting->pq_setting.update = 1; + + if (pqlt_cur_setting->pq_setting.memcdemo == 5) { + //user define level + iris_info.update.demo_win_fi = true; //first panel on, enter MEMC setting + pr_debug("iris: first time configure user demo window for MEMC setting ---\n"); + } + + if (pqlt_cur_setting->pq_setting.peakingdemo == 5) { + //user define level + iris_reg_add(PEAKING_STARTWIN, (iris_info.peaking_demo_win_info.startx & 0x3fff) + ((iris_info.peaking_demo_win_info.starty & 0x3fff) << 16)); + iris_reg_add(PEAKING_ENDWIN, (iris_info.peaking_demo_win_info.endx & 0x3fff) + ((iris_info.peaking_demo_win_info.endy & 0x3fff) << 16)); + iris_reg_add(PEAKING_CTRL, 1 | iris_info.peaking_demo_win_info.sharpness_en<<1); + iris_reg_add(PEAKING_SHADOW_UPDATE, 1); + pr_debug("iris: first time configure user demo window for peaking setting ---\n"); + } + } + + /*dbc setting*/ + if (pqlt_def_setting->dbc_setting.brightness != pqlt_cur_setting->dbc_setting.brightness) { + pr_debug("dbc brightness update\n"); + iris_info.update.dbc_setting = true; + } else if (pqlt_def_setting->dbc_setting.ext_pwm != pqlt_cur_setting->dbc_setting.ext_pwm) { + pr_debug("external pwm update\n"); + iris_info.update.dbc_setting = true; + } else if (pqlt_def_setting->dbc_setting.cabcmode != pqlt_cur_setting->dbc_setting.cabcmode) { + pr_debug("dbc quality update\n"); + iris_info.update.dbc_setting = true; + } else if (pqlt_def_setting->dbc_setting.dlv_sensitivity != pqlt_cur_setting->dbc_setting.dlv_sensitivity) { + pr_debug("dlv update\n"); + iris_info.update.dbc_setting = true; + } + + if (iris_info.update.dbc_setting) { + pqlt_cur_setting->dbc_setting.update = 1; + } + + /*lp memc*/ + if (pqlt_def_setting->lp_memc_setting.value != pqlt_cur_setting->lp_memc_setting.value) { + pr_debug("lp memc update\n"); + iris_info.update.lp_memc_setting = true; + } else if (pqlt_def_setting->lp_memc_setting.level != pqlt_cur_setting->lp_memc_setting.level) { + pr_debug("lp level update\n"); + iris_info.update.lp_memc_setting = true; + } + + if (iris_info.update.lp_memc_setting) { + pqlt_cur_setting->lp_memc_setting.update = 1; + } + + /*color adjust*/ + if(pqlt_def_setting->color_adjust != pqlt_cur_setting->color_adjust) { + pr_debug("color temperature update\n"); + iris_info.update.color_adjust = true; + } + + /*lce setting*/ + if (pqlt_def_setting->lce_setting.mode != pqlt_cur_setting->lce_setting.mode) { + pr_debug("lce mode update\n"); + iris_info.update.lce_setting = true; + } else if (pqlt_def_setting->lce_setting.mode1level != pqlt_cur_setting->lce_setting.mode1level) { + pr_debug("lce mode1 level update\n"); + iris_info.update.lce_setting = true; + } else if (pqlt_def_setting->lce_setting.mode2level != pqlt_cur_setting->lce_setting.mode2level) { + pr_debug("lce mode2 level update\n"); + iris_info.update.lce_setting = true; + } else if (pqlt_def_setting->lce_setting.demomode != pqlt_cur_setting->lce_setting.demomode) { + pr_debug("lce demo mode update\n"); + iris_info.update.lce_setting = true; + } + + if (iris_info.update.lce_setting) { + pqlt_cur_setting->lce_setting.update = 1; + } + + /*cm setting*/ + if (pqlt_def_setting->cm_setting.cm6axes != pqlt_cur_setting->cm_setting.cm6axes) { + pr_debug("6 axes cm update\n"); + iris_info.update.cm_setting = true; + } else if (pqlt_def_setting->cm_setting.cm3d != pqlt_cur_setting->cm_setting.cm3d) { + pr_debug("3d cm update\n"); + iris_info.update.cm_setting = true; + } else if (pqlt_def_setting->cm_setting.ftc_en != pqlt_cur_setting->cm_setting.ftc_en) { + pr_debug("ftc enable update\n"); + iris_info.update.cm_setting = true; + } else if (pqlt_def_setting->cm_setting.demomode != pqlt_cur_setting->cm_setting.demomode) { + pr_debug("cm demo mode update\n"); + iris_info.update.cm_setting = true; + } + + if (iris_info.update.cm_setting) { + pqlt_cur_setting->cm_setting.update = 1; + + if (pqlt_cur_setting->cm_setting.demomode == 5) { + //user define level + iris_reg_add(CM_STARTWIN, (iris_info.cm_demo_win_info.startx & 0x0fff) + ((iris_info.cm_demo_win_info.starty & 0x0fff) << 16)); + iris_reg_add(CM_ENDWIN, (iris_info.cm_demo_win_info.endx & 0x0fff) + ((iris_info.cm_demo_win_info.endy & 0x0fff) << 16)); + iris_reg_add(CM_SHADOW_UPDATE, 1); + pr_debug("iris: first time configure user demo window for cm setting ---\n"); + } + } + + //update dbc mode + if (iris_info.update.dbc_setting) { + if (pqlt_cur_setting->dbc_setting.cabcmode == 0) + iris_info.setting_info.dbc_mode &= ~(1 << 1); + else + iris_info.setting_info.dbc_mode |= 1 << 1; + if (pqlt_cur_setting->dbc_setting.dlv_sensitivity == 0) + iris_info.setting_info.dbc_mode &= ~1; + else + iris_info.setting_info.dbc_mode |= 1; + } + mutex_unlock(&iris_cfg->config_mutex); +} + +u32 iris_get_vtotal(struct iris_timing_info *info) +{ + u32 vtotal; + + vtotal = info->vfp + info->vsw + info->vbp + info->vres; + + return vtotal; +} + +u32 iris_get_htotal(struct iris_timing_info *info) +{ + u32 htotal; + + htotal = info->hfp + info->hsw + info->hbp + info->hres; + + return htotal; +} + +void iris_mcuclk_divider_change(struct mdss_dsi_ctrl_pdata *ctrl, char lowMcu) +{ + int switchenable = 0; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + struct quality_setting * pqlt_cur_setting = & iris_info.setting_info.quality_cur; + + mutex_lock(&iris_cfg->cmd_mutex); + if((pqlt_cur_setting->dbc_setting.dlv_sensitivity == 0x0) && + (pqlt_cur_setting->dbc_setting.cabcmode == 0x0)) + { + switchenable = 1; + pr_info("could switch to lower mcu clock\n"); + } + pr_info("Sensitive: %x, dvQu: %x\n",pqlt_cur_setting->dbc_setting.dlv_sensitivity,pqlt_cur_setting->dbc_setting.cabcmode ); + // FIXME: iris2-40p needs rework + /* + if(lowMcu && switchenable) + iris_reg_add(IRIS_SYS_ADDR+0x218, 0x10901); + else + iris_reg_add(IRIS_SYS_ADDR+0x218, 0x1); + iris_reg_add(IRIS_SYS_ADDR + 0x10, 1); //reg_update + iris_reg_add(IRIS_SYS_ADDR + 0x10, 0); //reg_update + */ + mutex_unlock(&iris_cfg->cmd_mutex); + pr_info("iris: %s, lowMcu: %d\n", __func__, lowMcu); +} + +static void iris_cmds_tx(struct work_struct *data) +{ + struct iris_mgmt_t *mgmt = container_of(data, struct iris_mgmt_t, iris_worker); + if (mgmt->iris_handler) + mgmt->iris_handler(); +} + +#if !defined(FPGA_PLATFORM) +#if 0 +static u32 iris_pi_write(struct mdss_dsi_ctrl_pdata *ctrl, u32 addr, u32 value) +{ + struct dcs_cmd_req cmdreq; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + static char pwil_write[24] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x3), + 0x00, + 0x00, + PWIL_U16(0x2), + PWIL_U32(IRIS_PROXY_MB0_ADDR), //default set to proxy MB0 + PWIL_U32(0x00000000) + }; + static struct dsi_cmd_desc iris_pwil_write_cmd = { + { DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(pwil_write) }, pwil_write }; + + if (!iris_cfg->ready) { + pr_err("%s:%u: iris not ready!\n", __func__, __LINE__); + return -EINVAL; + } + + pr_debug("%s, addr: 0x%x, value: 0x%x\n", __func__, addr, value); + + pwil_write[16] = addr & 0xff; + pwil_write[17] = (addr >> 8) & 0xff; + pwil_write[18] = (addr >> 16) & 0xff; + pwil_write[19] = (addr >> 24) & 0xff; + pwil_write[20] = value & 0xff; + pwil_write[21] = (value >> 8) & 0xff; + pwil_write[22] = (value >> 16) & 0xff; + pwil_write[23] = (value >> 24) & 0xff; + + cmdreq.cmds = &iris_pwil_write_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_HS_MODE | CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + return 0; +} +#endif + +u32 iris_pi_read(struct mdss_dsi_ctrl_pdata *ctrl, u32 addr) +{ + u32 value; + + char pi_address[16] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('S', 'G', 'L', 'W'), + PWIL_U32(0x01), //valid body word(4bytes) + PWIL_U32(IRIS_PROXY_MB0_ADDR), // proxy MB0 + }; + + struct dsi_cmd_desc pi_read_addr_cmd[] = { + { { DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(pi_address) }, pi_address }, + }; + + char pi_read[1] = { 0x00 }; + struct dsi_cmd_desc pi_read_cmd = { + { DTYPE_GEN_READ1, 1, 0, 1, 0, sizeof(pi_read) }, pi_read + }; + + char read_buf[16]; //total 4*32bit register + struct dcs_cmd_req cmdreq; + + pi_address[12] = addr & 0xff; + pi_address[13] = (addr >> 8) & 0xff; + pi_address[14] = (addr >> 16) & 0xff; + pi_address[15] = (addr >> 24) & 0xff; + + cmdreq.cmds = pi_read_addr_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_HS_MODE | CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + cmdreq.cmds = &pi_read_cmd; + cmdreq.cmds_cnt = 1; + /* 8094 LP mode read is okay, HS mode read failure */ + cmdreq.flags = CMD_REQ_LP_MODE | CMD_REQ_RX | CMD_REQ_COMMIT | CMD_REQ_NO_MAX_PKT_SIZE; + cmdreq.rlen = 4; + cmdreq.rbuf = (char *)read_buf; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + value = ctrl->rx_buf.data[0] | (ctrl->rx_buf.data[1] << 8) | + (ctrl->rx_buf.data[2] << 16) | (ctrl->rx_buf.data[3] << 24); + + return value; +} +#endif + +int iris_register_write(struct msm_fb_data_type *mfd, u32 addr, u32 value) +{ + struct mdss_overlay_private *mdp5_data; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + static char pwil_write[24] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x3), + 0x00, + 0x00, + PWIL_U16(0x2), + PWIL_U32(IRIS_PROXY_MB0_ADDR), //default set to proxy MB0 + PWIL_U32(0x00000000) + }; + + static struct dsi_cmd_desc iris_pwil_write_cmd = { + { DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(pwil_write) }, pwil_write }; + + struct dcs_cmd_req cmdreq; + + if (!iris_cfg->ready) { + pr_err("%s:%u: iris not ready!\n", __func__, __LINE__); + return -EINVAL; + } + + if (mfd->panel_power_state == MDSS_PANEL_POWER_OFF) + return 0; + + mdp5_data = mfd_to_mdp5_data(mfd); + pdata = mdp5_data->ctl->panel_data; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + + pr_debug("%s, addr: 0x%x, value: 0x%x\n", __func__, addr, value); + + pwil_write[16] = addr & 0xff; + pwil_write[17] = (addr >> 8) & 0xff; + pwil_write[18] = (addr >> 16) & 0xff; + pwil_write[19] = (addr >> 24) & 0xff; + pwil_write[20] = value & 0xff; + pwil_write[21] = (value >> 8) & 0xff; + pwil_write[22] = (value >> 16) & 0xff; + pwil_write[23] = (value >> 24) & 0xff; + + cmdreq.cmds = &iris_pwil_write_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_HS_MODE | CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + if (MIPI_VIDEO_MODE == iris_info.work_mode.rx_mode) { + while ((atomic_read(&mfd->iris_conf.mode_switch_cnt))) + usleep_range(17000, 17000); + } + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + if (MIPI_VIDEO_MODE == iris_info.work_mode.rx_mode) { + /* wait 1 vsync to sure command issue */ + usleep_range(17000, 17000); + } + return 0; +} + +void iris_dtg_para_set(int input_mode, int output_mode) +{ + struct iris_setting_info *psetting = &iris_info.setting_info; + u32 vtotal = iris_get_vtotal(&iris_info.output_timing); + u32 htotal = iris_get_htotal(&iris_info.output_timing); + u32 vfp = iris_info.output_timing.vfp; + u32 ovs_lock_te_en =0, psr_mask = 0, evs_sel = 0; + u32 te_en = 0, te_interval = 0, te_sel = 0, sw_te_en = 0, sw_fix_te_en = 0, te_auto_adj_en = 0, te_ext_en = 0, te_ext_filter_en = 0, te_ext_filter_thr = 0; + u32 cmd_mode_en = 0, cm_hw_rfb_mode = 0, cm_hw_frc_mode = 0; + u32 dtg_en = 1, ivsa_sel = 1, vfp_adj_en = 1, dframe_ratio = 1, vframe_ratio = 1, lock_sel = 1; + u32 sw_dvs_period = (vtotal << 8); + u32 sw_te_scanline = 0, sw_te_scanline_frc = 0, te_ext_dly = 0, te_out_sel = 0, te_out_filter_thr = 0, te_out_filter_en = 0, te_ext_dly_frc = 0; + u32 te2ovs_dly = 0, te2ovs_dly_frc = 0; + u32 evs_dly = 6, evs_new_dly = 1; + u32 vfp_max = 0; + u32 vfp_extra = psetting->delta_period_max; + u32 lock_mode = 0; + u32 vres_mem = 0, psr_rd = 2, frc_rd = 4, scale_down = 2, dsc = 2, margin = 2, scale_up = 2; + u32 peaking = 2; + u32 i2o_dly = 0; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + //dtg 1.1 mode, video in + if (MIPI_VIDEO_MODE == input_mode) + { + evs_sel = 1; + psr_mask = 1; + iris_cfg->dtg_setting.dtg_delay = 3; + vfp_max = vfp + vfp_extra; + } + else if (MIPI_VIDEO_MODE == output_mode) + { + if (!iris_debug_dtg_v12) { + //dtg 1.3 mode, command in and video out + ovs_lock_te_en = 1; + cmd_mode_en = 1; + te_en = 1; + sw_fix_te_en = 1; + te_auto_adj_en = 1; + te2ovs_dly = vfp - 1; + te2ovs_dly_frc = (vtotal*3)/4; + iris_cfg->dtg_setting.dtg_delay = 2; + vfp_max = (te2ovs_dly_frc > vfp) ? te2ovs_dly_frc : vfp; + } else { + //dtg 1.2 mode, command in and video out + cmd_mode_en = 1; + cm_hw_frc_mode = 3; + cm_hw_rfb_mode = 3; + te_en = 1; + te_sel = 1; + sw_te_en = 1; + te_auto_adj_en = 1; + vres_mem = iris_lp_memc_calc(LEVEL_MAX - 1) >> 16; + i2o_dly = ((psr_rd > frc_rd? psr_rd: frc_rd) + scale_down + dsc + margin) * iris_info.input_timing.vres + + scale_up * iris_info.output_timing.vres + + peaking * vres_mem; + sw_te_scanline = vtotal * vres_mem - (i2o_dly - iris_info.output_timing.vbp * vres_mem - iris_info.output_timing.vsw * vres_mem); + sw_te_scanline /= vres_mem; + sw_te_scanline_frc = (vtotal)/4; + te_out_filter_thr = (vtotal)/2; + te_out_filter_en = 1; + iris_cfg->dtg_setting.dtg_delay = 2; + vfp_max = vfp + vfp_extra; + } + } + //dtg 1.4 mode, command in and command out + else if (MIPI_CMD_MODE == output_mode) + { + vfp_max = vfp + vfp_extra; + evs_dly = 2; + evs_sel = 1; + ovs_lock_te_en = 1; + cmd_mode_en = 1; + te_en = 1; + te_auto_adj_en = 1; + te_ext_en = 1; + + te_ext_filter_thr = (((u32)iris_info.output_timing.hres * (u32)iris_info.output_timing.vres * 100)/vtotal/htotal)*vtotal/100; + te2ovs_dly = 2; + te2ovs_dly_frc = 2; + te_ext_dly = 1; + te_out_sel = 1; + te_out_filter_thr = (vtotal)/2; + te_out_filter_en = 1; + te_ext_dly_frc = (vtotal)/4; + iris_cfg->dtg_setting.dtg_delay = 1; + te_ext_filter_en = 1; + lock_mode = 2; + + te_sel = 1; + sw_te_en = 1; + vres_mem = iris_lp_memc_calc(LEVEL_MAX - 1) >> 16; + i2o_dly = ((psr_rd > frc_rd? psr_rd: frc_rd) + scale_down + dsc + margin) * iris_info.input_timing.vres + + scale_up * iris_info.output_timing.vres + + peaking * vres_mem; + sw_te_scanline = vtotal * vres_mem - (i2o_dly - iris_info.output_timing.vbp * vres_mem - iris_info.output_timing.vsw * vres_mem); + sw_te_scanline /= vres_mem; + sw_te_scanline_frc = (vtotal)/4; + evs_new_dly = (scale_down + dsc + margin) * iris_info.input_timing.vres / vres_mem - te2ovs_dly; + } + + iris_cfg->dtg_setting.dtg_ctrl = dtg_en + (ivsa_sel << 3) + (dframe_ratio << 4) + (vframe_ratio << 9) + (vfp_adj_en << 17) + + (ovs_lock_te_en << 18) + (lock_sel << 26) + (evs_sel << 28) + (psr_mask << 30); + iris_cfg->dtg_setting.dtg_ctrl_1 = (cmd_mode_en) + (lock_mode << 5) + (cm_hw_rfb_mode << 10) + (cm_hw_frc_mode << 12); + iris_cfg->dtg_setting.evs_dly = evs_dly; + iris_cfg->dtg_setting.evs_new_dly = evs_new_dly; + iris_cfg->dtg_setting.te_ctrl = (te_en) + (te_interval << 1) + (te_sel << 2) + (sw_te_en << 3) + (sw_fix_te_en << 5) + + (te_auto_adj_en << 6) + (te_ext_en << 7) + (te_ext_filter_en << 8) + (te_ext_filter_thr << 9); + iris_cfg->dtg_setting.dvs_ctrl = sw_dvs_period; + iris_cfg->dtg_setting.te_ctrl_1 = sw_te_scanline; + iris_cfg->dtg_setting.te_ctrl_2 = sw_te_scanline_frc; + iris_cfg->dtg_setting.te_ctrl_3 = te_ext_dly + (te_out_sel << 24); + iris_cfg->dtg_setting.te_ctrl_4 = te_out_filter_thr + (te_out_filter_en << 24); + iris_cfg->dtg_setting.te_ctrl_5 = te_ext_dly_frc; + iris_cfg->dtg_setting.te_dly = te2ovs_dly; + iris_cfg->dtg_setting.te_dly_1 = te2ovs_dly_frc; + iris_cfg->dtg_setting.vfp_ctrl_0 = vfp + (1<<24); + iris_cfg->dtg_setting.vfp_ctrl_1 = vfp_max; +} + +int iris_set_configure(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + struct quality_setting * pqlt_cur_setting = &iris_info.setting_info.quality_cur; + // FIXME + if (iris_cfg->sf_notify_mode == IRIS_MODE_FRC_PREPARE || + iris_cfg->sf_notify_mode == IRIS_MODE_FRC_PREPARE_DONE) + return 0; + + // no update + if (!iris_info.update.pq_setting && !iris_info.update.dbc_setting + && !iris_info.update.lp_memc_setting && !iris_info.update.color_adjust + && !iris_info.update.lce_setting &&!iris_info.update.cm_setting) + return 0; + + mutex_lock(&iris_cfg->config_mutex); + // PQ setting, MB3 + if (iris_info.update.pq_setting) { + iris_reg_add(IRIS_PQ_SETTING_ADDR, *((u32 *)&pqlt_cur_setting->pq_setting)); + pr_info("%s, %d: configValue = %d.\n", __func__, __LINE__, *((u32 *)&pqlt_cur_setting->pq_setting)); + iris_info.update.pq_setting = false; + } + + // DBC setting, MB5 + if (iris_info.update.dbc_setting) { + iris_reg_add(IRIS_DBC_SETTING_ADDR, *((u32 *)&pqlt_cur_setting->dbc_setting)); + iris_info.update.dbc_setting = false; + } + + if (iris_info.update.lp_memc_setting) { + iris_reg_add(IRIS_LPMEMC_SETTING_ADDR, pqlt_cur_setting->lp_memc_setting.value | 0x80000000); + iris_info.update.lp_memc_setting = false; + } + + if (iris_info.update.color_adjust) { + iris_reg_add(IRIS_COLOR_ADJUST_ADDR, (u32)pqlt_cur_setting->color_adjust | 0x80000000); + iris_info.update.color_adjust = false; + } + //LCE Setting,DSC_ENCODER_ALG_PARM2 + if (iris_info.update.lce_setting) { + iris_reg_add(IRIS_LCE_SETTING_ADDR, *((u32 *)&pqlt_cur_setting->lce_setting)); + iris_info.update.lce_setting = false; + } + + // CM Setting,DSC_ENCODER_ALG_PARM6 + if (iris_info.update.cm_setting) { + iris_reg_add(IRIS_CM_SETTING_ADDR, *((u32 *)&pqlt_cur_setting->cm_setting)); + iris_info.update.cm_setting = false; + } + + mutex_unlock(&iris_cfg->config_mutex); + + return 0; +} + + +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) +int iris_Drc_LPMemc_update(struct msm_fb_data_type *mfd) +{ + + u32 configAddr = 0; + u32 configValue = 0; + + configValue = g_mfd->iris_conf.drc_size | 0x80000000; + configAddr = IRIS_DRC_INFO_ADDR; + + if (0 == configValue && 0 == configAddr) { + pr_warn("iris_Drc_LPMemc_update failed!\n"); + return -EINVAL; + } + + return iris_register_write(mfd, configAddr, configValue); + +} + +int iris_get_frc_timing(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret = -1; + pr_debug("frc low power timing = %x\n", mfd->iris_conf.lp_frc_timing); + ret = copy_to_user(argp, &(mfd->iris_conf.lp_frc_timing), sizeof(uint32_t)); + //TODO + return ret; +} + +void iris_calc_drc_exit(struct msm_fb_data_type *mfd) +{ + u32 configAddr = 0; + u32 configValue = 0; + +#if defined(FPGA_PLATFORM) + configValue = 0x00840200; +#else + configValue = iris_pi_read(g_dsi_ctrl, IRIS_DATA_PATH_ADDR); + configValue = configValue & 0xFFFF7FFF;//clear bit 15 +#endif + iris_register_write(mfd, configAddr, configValue); + return; +} +#endif + +static bool iris_is_cmdin_cmdout(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + + if ((MIPI_CMD_MODE == pwork_mode->rx_mode) && + (MIPI_CMD_MODE == pwork_mode->tx_mode)) + return true; + else + return false; +} + +bool iris_is_cmdin_videout(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + + if ((MIPI_CMD_MODE == pwork_mode->rx_mode) && + (MIPI_VIDEO_MODE == pwork_mode->tx_mode)) + return true; + else + return false; +} + +static bool iris_is_videoin_videout(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + + if ((MIPI_VIDEO_MODE == pwork_mode->rx_mode) && + (MIPI_VIDEO_MODE == pwork_mode->tx_mode)) + return true; + else + return false; +} + +static bool iris_is_scale_enable(void) +{ + if (iris_info.input_timing.hres == iris_info.output_timing.hres && + iris_info.output_timing.vres == iris_info.output_timing.vres) + return false; + else + return true; +} + +static bool iris_pt_available(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + + if (iris_cfg->ready && !iris_is_scale_enable() && + (iris_is_cmdin_cmdout() || iris_is_videoin_videout())) { + iris_cfg->avail_mode.kickoff60_request = false; + iris_cfg->avail_mode.last_frame_repeat_cnt = 0; + iris_cfg->avail_mode.pt_available = true; + return true; + } else { + iris_cfg->avail_mode.pt_available = false; + return false; + } +} + +bool iris_is_dbc_setting_disable(void) +{ + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_dbc_setting * dbc_setting = &psetting_info->quality_cur.dbc_setting; + if (dbc_setting->dlv_sensitivity == pdisable_info->dbc_dlv_sensitivity_disable_val && + dbc_setting->cabcmode == pdisable_info->dbc_quality_disable_val) { + return true; + } + return false; +} + + +static bool iris_is_pq_setting_disable(void) +{ + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_pq_setting * pq_setting = &psetting_info->quality_cur.pq_setting; + + /*TODO: add following value + CM 6-Axis Level + CM 3D LUT Level + CM Demo Mode + CM Flesh Tone EN*/ + if (pq_setting->peaking == pdisable_info->pq_peaking_disable_val && + pq_setting->peakingdemo == pdisable_info->pq_peaking_demo_disable_val && + pq_setting->gamma == pdisable_info->pq_gamma_disable_val && + pq_setting->contrast == pdisable_info->pq_contrast_disable_val ) { + return true; + } + return false; +} + +bool iris_is_peaking_setting_disable(void) +{ + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_pq_setting * pq_setting = &psetting_info->quality_cur.pq_setting; + + return pq_setting->peaking == pdisable_info->pq_peaking_disable_val; +} + +bool iris_is_lce_setting_disable(void) +{ + //TODO: + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_lce_setting * lce_setting = &psetting_info->quality_cur.lce_setting; + + if (lce_setting->mode == pdisable_info->lce_mode_disable_val) + return true; + + return false; +} + +bool iris_is_cm_setting_disable(void) +{ + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_cm_setting * cm_setting = &psetting_info->quality_cur.cm_setting; + + if (pdisable_info->cm_c6axes_disable_val == cm_setting->cm6axes && + pdisable_info->cm_c3d_disable_val == cm_setting->cm3d) + return true; + + return false; +} + +static bool iris_bypass_available(struct msm_fb_data_type *mfd) +{ + + struct iris_config *iris_cfg = &mfd->iris_conf; + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + + /* setting is close + * input_timing equal to output_timing + * rx mode equal tx mode + */ + if (iris_cfg->ready && + iris_is_lce_setting_disable() && + iris_is_dbc_setting_disable() && + iris_is_pq_setting_disable() && + iris_is_cm_setting_disable() && + (iris_is_cmdin_cmdout() || iris_is_videoin_videout()) && + pdisable_info->color_adjust_disable_val == psetting_info->quality_cur.color_adjust) { + iris_cfg->avail_mode.bypass_available = true; + return true; + } else { + iris_cfg->avail_mode.bypass_available = false; + return false; + } +} + +int iris_get_available_mode(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret = -1; + struct iris_available_mode dbg_iris_avail_mode; + struct iris_setting_info * psetting_info = &iris_info.setting_info; + struct iris_setting_disable_info *pdisable_info = & psetting_info->disable_info; + struct iris_config *iris_cfg = &mfd->iris_conf; + struct iris_available_mode * piris_avail_mode = &iris_cfg->avail_mode; + + if (MIPI_VIDEO_MODE == iris_info.work_mode.tx_mode) { + piris_avail_mode->pt_threshold_low = 0; + piris_avail_mode->rfb_threshold_low = 0; + } + + if (iris_pt_available(mfd)) { + if (iris_is_cmdin_cmdout()) { + //TODO judge LCE and DBC is open then + if (!iris_is_lce_setting_disable() || !iris_is_dbc_setting_disable()) { + piris_avail_mode->last_frame_repeat_cnt = pdisable_info->last_frame_repeat_cnt; + piris_avail_mode->prefer_mode = IRIS_MODE_RFB; + } + else + piris_avail_mode->prefer_mode = IRIS_MODE_PT; + + } else if (iris_is_cmdin_videout()) { + piris_avail_mode->kickoff60_request = true; + } + } + + if (iris_bypass_available(mfd)) { + pr_debug("%s bypass is available \n", __func__); + } + + memcpy(&dbg_iris_avail_mode, &iris_cfg->avail_mode, sizeof(struct iris_available_mode)); + + if (iris_debug_pt) + dbg_iris_avail_mode.pt_available = true; + if (iris_debug_bypass) + dbg_iris_avail_mode.bypass_available = true; + if (iris_debug_kickoff60) + dbg_iris_avail_mode.kickoff60_request = true; + if (iris_debug_lastframerepeat) + dbg_iris_avail_mode.last_frame_repeat_cnt = pdisable_info->last_frame_repeat_cnt; + if (iris_debug_pt_disable) + dbg_iris_avail_mode.pt_available = false; + + if (iris_debug_pt || iris_debug_bypass || iris_debug_kickoff60 || + iris_debug_lastframerepeat || iris_debug_pt_disable) { + ret = copy_to_user(argp, &dbg_iris_avail_mode, sizeof(struct iris_available_mode)); + } else { + pr_debug("pt_avail = %d, bypass_avail = %d kickoff60 = %d last_frame= %d\n", + piris_avail_mode->pt_available, piris_avail_mode->bypass_available, + piris_avail_mode->kickoff60_request, piris_avail_mode->last_frame_repeat_cnt); + ret = copy_to_user(argp, piris_avail_mode, sizeof(struct iris_available_mode)); + } + return ret; +} + + +static inline u32 mdss_mdp_cmd_vsync_count(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_mixer *mixer; + u32 cnt = 0xffff; /* init to an invalid value */ + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + + mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); + if (!mixer) { + mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT); + if (!mixer) { + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + goto exit; + } + } + cnt = (mdss_mdp_pingpong_read(mixer->pingpong_base, MDSS_MDP_REG_PP_INT_COUNT_VAL) >> 16) & 0xffff; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + +exit: + return cnt; +} + +static void iris_proc_te(struct iris_config *iris_cfg, u32 fcnt, u32 lcnt, u32 fps, u32 vper) +{ + static u32 fcnt0, lcnt0; + static u64 time0; + static u32 te_period; + ktime_t ktime = ktime_get(); + u64 time = ktime_to_us(ktime); + + if (fcnt - fcnt0 >= 1200) { + if (time - time0) { + u32 detla_t = time - time0; + te_period = ((fcnt - fcnt0) * vper + lcnt - lcnt0)*1000/fps/(detla_t/1000); + pr_debug("te_period=%u\n", te_period); + if (abs(te_period - vper) > (vper >> 5)) + te_period = vper; + } + fcnt0 = fcnt; + lcnt0 = lcnt; + time0 = time; + } + + //if (!te_period) + te_period = vper; + + iris_cfg->meta.te_period = te_period; + pr_debug("fcnt %u fcnt0 %u lcnt %u lcnt0 %u fps %u\n", fcnt, fcnt0, lcnt, lcnt0, fps); + pr_debug("time %llu time0 %llu\n", time, time0); + pr_debug("te %u vper %u\n", te_period, vper); +} + +static int iris_vary_te(struct iris_config *iris_cfg, u32 fcnt, int vper) +{ +#define THRESHOLD 0 +#define FRAME_CNT 120 +#define PLAYING 0x01 +#define FIRST_FRAME 0x04 + static u32 fcnt0, vts0, time0, sts0; + static bool player_sts; + u32 time = iris_cfg->meta.sys_ts; + u32 vts = iris_cfg->meta.video_ts; + int delta_time, delta_period; + int delta_t, delta_v, delta_sts, ret_val = false; + ktime_t ktime = ktime_get(); + u32 sts = (u32) ktime_to_us(ktime); + + pr_debug("meta.op=0x%x, meta.flags=0x%x, meta.video_ts=%u, delta_period_range(%d, %d) \n", + iris_cfg->meta.op, iris_cfg->meta.flags, iris_cfg->meta.video_ts, + iris_info.setting_info.delta_period_max, iris_info.setting_info.delta_period_min); + + if (!(iris_cfg->meta.op & MDP_IRIS_OP_FLG)) { + pr_debug("flag invalid\n"); + if (iris_cfg->sw_te_period != vper) { + iris_cfg->sw_te_period = vper; + ret_val = true; + } + return ret_val; + } + iris_cfg->meta.op &= ~MDP_IRIS_OP_FLG; + + if (!(iris_cfg->meta.flags & PLAYING)) { + if (player_sts) + pr_debug("play stop\n"); + //if video is stopped, retore TE to 60hz + player_sts = 0; + if (iris_cfg->sw_te_period != vper) { + iris_cfg->sw_te_period = vper; + ret_val = true; + } + return ret_val; + } + + //get reference frame + if (iris_cfg->meta.flags & FIRST_FRAME) { + player_sts = 1; + vts0 = iris_cfg->meta.video_ts; + time0 = time; + fcnt0 = fcnt; + sts0 = sts; + pr_debug("get reference frame ats0 %u vts0 %u sts0 %u f0 %u\n", time0, vts0, sts0, fcnt0); + } + + delta_t = time - time0; + delta_v = vts - vts0; + delta_sts = sts - sts0; + delta_time = delta_v - delta_t; + + if ((fcnt - fcnt0 >= FRAME_CNT) && vts && delta_t && delta_v && player_sts) { + if (iris_cfg->current_mode != IRIS_FRC_MODE) { + pr_debug("not in FRC mode\n"); + if (iris_cfg->sw_te_period != vper) { + iris_cfg->sw_te_period = vper; + ret_val = true; + } + } else if (abs(delta_v - delta_t) > THRESHOLD) { + u32 sw_te_period_prev = iris_cfg->sw_te_period; + // line_time = 1000000us / (60 * vper); + // delta_period = delta_time / line_time; + delta_period = (delta_time * vper) / 16667; + delta_period = DIV_ROUND_CLOSEST(delta_period, FRAME_CNT); + + if (delta_period < iris_info.setting_info.delta_period_min) { + pr_debug("delta_period:%d out of min range\n", delta_period); + delta_period = iris_info.setting_info.delta_period_min; + } else if (delta_period > iris_info.setting_info.delta_period_max) { + pr_debug("delta_period:%d out of max range\n", delta_period); + delta_period = iris_info.setting_info.delta_period_max; + } + iris_cfg->sw_te_period = vper + delta_period; + ret_val = sw_te_period_prev != iris_cfg->sw_te_period; + } + pr_debug("fcnt %u fcnt0 %u vts %u vts0 %u delta_v %u\n", fcnt, fcnt0, vts, vts0, delta_v); + pr_debug("time %u time0 %u delta_t %u\n", time, time0, delta_t); + pr_debug("sts %u sts0 %u delta_sts %u\n", sts, sts0, delta_sts); + pr_debug("delta_time %i delta_period %i vper %u ret_val %u\n", delta_time, delta_period, vper, ret_val); + + fcnt0 = fcnt; + } + return ret_val; +} + +static void iris_proc_ct(struct iris_config *iris_cfg, u32 fps) +{ + u32 prev_vts; + u32 vts; + u32 te; + + prev_vts = iris_cfg->prev_vts; + vts = iris_cfg->meta.video_ts; + te = iris_cfg->meta.te_period; + iris_cfg->meta.content_period = (vts - prev_vts) * fps / 1000 * te / 1000; + iris_cfg->meta.content_period_frac = (((vts - prev_vts) * fps / 1000 * te) & 0xfff) / (1000 >> 8); + iris_cfg->meta.content_period_frac &= 0xff; +} + +static void iris_proc_vp(struct iris_config *iris_cfg) +{ + iris_cfg->meta.vs_period = iris_cfg->meta.te_period; + iris_cfg->meta.vs_period_frac = 0; +} + +static void iris_proc_sts(struct iris_config *iris_cfg, u32 fps, u32 lcnt, u32 vper) +{ + if (iris_cfg->meta.op & MDP_IRIS_OP_STS) { + pr_debug("sts %u\n", iris_cfg->meta.sys_ts); + return; + } else { + u32 sts; + ktime_t ktime = ktime_get(); + u64 time = ktime_to_us(ktime); + sts = (u32) time; + sts -= 1000000000 / fps / vper * lcnt / 1000; + iris_cfg->meta.sys_ts = sts; + return; + } +} + +static void iris_proc_restart(struct iris_config *iris_cfg) +{ + if (!(iris_cfg->meta.op & MDP_IRIS_OP_RESTART)) + iris_cfg->meta.restart = 1; + else + iris_cfg->meta.restart = (iris_cfg->prev_vts == iris_cfg->meta.video_ts); +} + +static int iris_proc_vts(struct iris_config *iris_cfg) +{ + int ret; + if (!(iris_cfg->meta.op & MDP_IRIS_OP_VTS)) + return 0; + + ret = (iris_cfg->prev_vts != iris_cfg->meta.video_ts); + iris_cfg->prev_vts = iris_cfg->meta.video_ts; + return ret; +} + +void iris_set_te(struct iris_config *iris_cfg, int te_flag) +{ + if (!debug_te_enabled || !te_flag) + return; + + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_DTG_ADDR + 0x00090, (iris_cfg->sw_te_period << 8 | 2 << 30)); //DVS_CTRL + iris_reg_add(IRIS_DTG_ADDR + 0x10000, 1); //reg_update + mutex_unlock(&iris_cfg->cmd_mutex); + pr_debug("set_te: %d\n", iris_cfg->sw_te_period); +} + +static void iris_set_dtg(struct iris_config *iris_cfg) +{ + if (!debug_dtg_enabled) + return; + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_DTG_ADDR + 0x10004, iris_cfg->meta.sys_ts); + iris_reg_add(IRIS_DTG_ADDR + 0x10008, iris_cfg->meta.video_ts); + iris_reg_add(IRIS_DTG_ADDR + 0x1000c, ((iris_cfg->meta.vs_period & 0xffff) << 8 | + (iris_cfg->meta.vs_period_frac & 0xff))); + iris_reg_add(IRIS_DTG_ADDR + 0x10010, iris_cfg->meta.te_period); + iris_reg_add(IRIS_DTG_ADDR + 0x10014, ((iris_cfg->meta.content_period & 0xffff) << 8 | + (iris_cfg->meta.content_period_frac & 0xff))); + iris_reg_add(IRIS_DTG_ADDR + 0x10018, ((iris_cfg->meta.restart & 1) << 8 | + (iris_cfg->meta.motion & 0xff))); + iris_reg_add(IRIS_DTG_ADDR + 0x1001c, 1); + mutex_unlock(&iris_cfg->cmd_mutex); + pr_debug("dtg set\n"); +} + +static void iris_proc_scale(struct iris_config *iris_cfg, u32 dvts, u32 prev_dvts) +{ + u32 scale; + if (abs(dvts-prev_dvts) <= ((dvts + prev_dvts) >> 5)) + scale = 64; + else { + scale = (dvts * 64 + prev_dvts / 2) / prev_dvts; + scale = min((u32)255, scale); + scale = max((u32)16, scale); + } + iris_cfg->scale = scale; + pr_debug("pdvts %u dvts %u scale %u\n", prev_dvts, dvts, scale); +} + +static void iris_set_constant_ratio(struct iris_config *iris_cfg) +{ + unsigned int reg_in, reg_out, reg_scale, reg_cap; + + reg_in = iris_cfg->in_ratio << IRIS_PWIL_IN_FRAME_SHIFT | (1 << 15); + reg_out = iris_cfg->out_ratio << IRIS_PWIL_OUT_FRAME_SHIFT; + reg_scale = 4096/iris_cfg->scale << 24 | 64 << 16 | iris_cfg->scale << 8 | iris_cfg->scale; + /* duplicated video frame */ + reg_cap = (iris_cfg->meta.repeat != IRIS_REPEAT_CAPDIS) << 1; + reg_cap |= 0xc0000001; + iris_cfg->iris_ratio_updated = true; + + pr_debug("reg_cap 0x%08x\n", reg_cap); + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x12FC, reg_in); + iris_reg_add(IRIS_PWIL_ADDR + 0x0638, reg_out); + if (debug_new_repeat == 0) + iris_reg_add(IRIS_PWIL_ADDR + 0x0218, reg_cap); + iris_reg_add(IRIS_PWIL_ADDR + 0x10000, (1 << 8) | (1 << 6)); + iris_reg_add(IRIS_MVC_ADDR + 0x1D0, reg_scale); + iris_reg_add(IRIS_MVC_ADDR + 0x1FF00, 1); + mutex_unlock(&iris_cfg->cmd_mutex); +} + +int iris_proc_constant_ratio(struct iris_config *iris_cfg) +{ + u32 dvts, in_t, out_t; + uint32_t r; + + dvts = 1000000 / iris_cfg->input_frame_rate; + in_t = (dvts * iris_cfg->output_frame_rate + 50000) / 100000; + out_t = 10; + + r = gcd(in_t, out_t); + pr_debug("in_t %u out_t %u r %u\n", in_t, out_t, r); + iris_cfg->in_ratio = out_t / r; + iris_cfg->out_ratio = in_t / r; + iris_proc_scale(iris_cfg, dvts, dvts); + // in true-cut case, always keep 1:1 + if (iris_cfg->true_cut_enable) { + iris_cfg->in_ratio = 1; + iris_cfg->out_ratio = 1; + iris_cfg->scale = 64; + } + pr_debug("in/out %u:%u\n", iris_cfg->in_ratio, iris_cfg->out_ratio); + // update register + iris_set_constant_ratio(iris_cfg); + + return 0; +} + +static int iris_proc_ratio(struct iris_config *iris_cfg) +{ + int ret = 0; + u32 prev_dvts; + u32 dvts, in_t, out_t; + uint32_t r; + + if (!(iris_cfg->meta.op & MDP_IRIS_OP_VTS)) + return 0; + + dvts = iris_cfg->meta.video_ts - iris_cfg->prev_vts; + prev_dvts = iris_cfg->prev_dvts; + + pr_debug("vts %u pvts %u dvts %u\n", iris_cfg->meta.video_ts, iris_cfg->prev_vts, dvts); + if (dvts > 200000) + return 0; + + if ((iris_cfg->iris_ratio_updated == true) && (abs(dvts - prev_dvts) < 3000)) + return 0; + + if (iris_cfg->repeat == IRIS_REPEAT_FORCE) + return 0; + + if (debug_hlmd_enabled && !iris_cfg->true_cut_enable) { + pr_debug("enable hlmd function.\n"); + // constant ratio + ret = iris_proc_constant_ratio(iris_cfg); + iris_cfg->prev_dvts = dvts; + return ret; + } else { + pr_debug("don't enable hlmd function.\n"); + if (!iris_cfg->meta.video_ts || !debug_ratio_enabled) + return 0; + } + + if (prev_dvts && dvts) { + in_t = (dvts * iris_cfg->output_frame_rate + 50000) / 100000; + out_t = 10; + + r = gcd(in_t, out_t); + pr_debug("in_t %u out_t %u r %u\n", in_t, out_t, r); + iris_cfg->in_ratio = out_t / r; + iris_cfg->out_ratio = in_t / r; + iris_proc_scale(iris_cfg, dvts, prev_dvts); + iris_cfg->iris_ratio_updated = (abs(dvts - prev_dvts) < 3000) ? true : false; + ret = 1; + pr_debug("in/out %u:%u\n", iris_cfg->in_ratio, iris_cfg->out_ratio); + } + + if (prev_dvts && !dvts) + ret = 1; + + if (dvts) + iris_cfg->prev_dvts = dvts; + + return ret; +} + +void iris_calc_nrv(struct mdss_mdp_ctl *ctl) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + uint16_t width, height; + +#define VIDEO_CTRL3 0x104c +#define VIDEO_CTRL4 0x1050 +#define VIDEO_CTRL5 0x1054 +#define VIDEO_CTRL11 0x106c +#define VIDEO_CTRL12 0x1070 +#define DISP_CTRL2 0x120c +#define REG_UPDATE 0x10000 + + if (iris_cfg->meta.op & MDP_IRIS_OP_NRV) { + iris_reg_add(IRIS_PWIL_ADDR + VIDEO_CTRL3, ((uint32_t)iris_cfg->meta.nrv.captureTop << 16) | + (uint32_t)iris_cfg->meta.nrv.captureLeft); + width = iris_cfg->meta.nrv.captureRight - iris_cfg->meta.nrv.captureLeft; + height = iris_cfg->meta.nrv.captureBottom - iris_cfg->meta.nrv.captureTop; + iris_reg_add(IRIS_PWIL_ADDR + VIDEO_CTRL4, ((uint32_t)height << 16) | (uint32_t)width); + iris_reg_add(IRIS_PWIL_ADDR + VIDEO_CTRL5, ((uint32_t)height << 16) | (uint32_t)width); + iris_reg_add(IRIS_PWIL_ADDR + VIDEO_CTRL11, ((uint32_t)height << 16) | (uint32_t)width); + iris_reg_add(IRIS_PWIL_ADDR + VIDEO_CTRL12, ((uint32_t)height << 16) | (uint32_t)width); + iris_reg_add(IRIS_PWIL_ADDR + DISP_CTRL2, ((uint32_t)iris_cfg->meta.nrv.displayTop << 16) | + (uint32_t)iris_cfg->meta.nrv.displayLeft); + iris_reg_add(IRIS_PWIL_ADDR + REG_UPDATE, 0x100); + + width = iris_cfg->meta.nrv.displayRight - iris_cfg->meta.nrv.displayLeft; + height = iris_cfg->meta.nrv.displayBottom - iris_cfg->meta.nrv.displayTop; + iris_reg_add(IRIS_NRV_INFO1_ADDR, ((uint32_t)height << 16) | (uint32_t)width); + iris_reg_add(IRIS_NRV_INFO2_ADDR, ((uint32_t)iris_cfg->meta.nrv.displayTop << 16) | + (uint32_t)iris_cfg->meta.nrv.displayLeft); + iris_cfg->nrv_enable = iris_cfg->meta.nrv.nrvEnable; + } +} + +static void iris_calc_true_cut(struct msm_fb_data_type *mfd) { + struct iris_config *iris_cfg = &mfd->iris_conf; + if (iris_cfg->meta.op & MDP_IRIS_OP_IF1) { + uint32_t info_header = iris_cfg->meta.iris_info1 >> 28; + + pr_debug("true cut: %x\n", iris_cfg->meta.iris_info1); + + if (info_header == 0x8 || info_header == 0x9 || + info_header == 0xa || info_header == 0xb) { + if (debug_true_cut) { + if (info_header == 0x8) + iris_cfg->input_vfr = 50; + else if (info_header == 0x9) + iris_cfg->input_vfr = 60; + else if (info_header == 0xa) + iris_cfg->input_vfr = 15; + else if (info_header == 0xb) + iris_cfg->input_vfr = 15; + iris_cfg->true_cut_enable = true; + if (iris_cfg->sf_notify_mode == IRIS_MODE_FRC) + iris_reg_add(IRIS_TRUECUT_INFO_ADDR, iris_cfg->meta.iris_info1); + } + } else { + iris_cfg->input_vfr = 0; + iris_cfg->true_cut_enable = false; + } + } +} +#if 0 +int iris_calc_meta(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_ctl *ctl = mdp5_data->ctl; + struct mdss_panel_data *pdata = mdp5_data->ctl->panel_data; + u32 fps, fcnt, lcnt; + u32 vper; + int ret = 0, te_flag = 0; + iris_calc_true_cut(ctl->mfd); + if ((atomic_read(&mfd->iris_conf.mode_switch_cnt))) { + iris_proc_vts(iris_cfg); + return ret; + } + + if (!debug_send_meta_enabled) + return ret; + // TODO + //if (iris_cfg->current_mode != IRIS_MEMC_MODE) + // return 0; + + fps = mdss_panel_get_framerate(&pdata->panel_info); + if (fps == 0) + return ret; + + vper = iris_get_vtotal(&iris_info.output_timing); + + lcnt = 0;//ctl->read_line_cnt_fnc(ctl); + if (pdata->panel_info.type == MIPI_CMD_PANEL) { + fcnt = mdss_mdp_cmd_vsync_count(ctl); + iris_proc_sts(iris_cfg, fps, lcnt, vper); + iris_proc_restart(iris_cfg); + iris_proc_te(iris_cfg, fcnt, lcnt, fps, vper); + te_flag = iris_vary_te(iris_cfg, fcnt, (int)vper); + iris_proc_ct(iris_cfg, fps); + iris_proc_vp(iris_cfg); + ret = iris_proc_vts(iris_cfg); + } else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) { + fcnt = mdss_mdp_video_vsync_count(ctl); + iris_proc_sts(iris_cfg, fps, lcnt, vper); + iris_proc_restart(iris_cfg); + ret = iris_proc_vts(iris_cfg); + } + + if (iris_is_cmdin_videout()) + iris_set_te(iris_cfg, te_flag); + + iris_set_dtg(iris_cfg); + + pr_debug("sts=%u fps=%u fcnt=%u vts=%u restart=%u in_ratio=%u out_ratio=%u\n", iris_cfg->meta.sys_ts, fps, fcnt, + iris_cfg->meta.video_ts, iris_cfg->meta.restart, iris_cfg->in_ratio, iris_cfg->out_ratio); + + memset((void *)&iris_cfg->meta, 0, sizeof(struct iris_meta)); + + return ret; +} +#endif + +static int iris_set_repeat(struct iris_config *iris_cfg) +{ + unsigned int reg_in, reg_out; + unsigned int val_frcc_cmd_th = iris_cfg->val_frcc_cmd_th; + unsigned int val_frcc_reg8 = iris_cfg->val_frcc_reg8; + unsigned int val_frcc_reg16 = iris_cfg->val_frcc_reg16; + bool cap_enable = true; + + // FIXME + if (!debug_repeat_enabled || iris_cfg->sf_notify_mode != IRIS_MODE_FRC) + return true; + + if ((iris_cfg->repeat == IRIS_REPEAT_FORCE) && (!frc_repeat_enter)) { + reg_in = (1 << IRIS_PWIL_IN_FRAME_SHIFT) | (1 << 15); + reg_out = 1 << IRIS_PWIL_OUT_FRAME_SHIFT; + val_frcc_cmd_th &= 0x1fffffff; + val_frcc_cmd_th |= 0x20000000; + frc_repeat_enter = true; + // iris2-40p use [15:8] for REPEATP1_TH iris2 use + // [13:8] Do replace 0x3f << 8 to 0xff << 8 + // and in design iris2-40p no need to do work around + val_frcc_reg8 |= 0xff00; + val_frcc_reg16 &= 0xffff7fff; + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(FRCC_CTRL_REG8_ADDR, val_frcc_reg8); + iris_reg_add(FRCC_CTRL_REG16_ADDR, val_frcc_reg16); + iris_reg_add(FRCC_CMD_MOD_TH, val_frcc_cmd_th); + iris_reg_add(FRCC_REG_SHOW, 0x2); + if (!iris_cfg->true_cut_enable) { // in true-cut case, always keep 1:1 + iris_reg_add(IRIS_PWIL_ADDR + 0x12FC, reg_in); + iris_reg_add(IRIS_PWIL_ADDR + 0x0638, reg_out); + iris_reg_add(IRIS_PWIL_ADDR + 0x10000, (1 << 8) | (1 << 6)); + } + mutex_unlock(&iris_cfg->cmd_mutex); + } else if (iris_cfg->repeat != IRIS_REPEAT_FORCE) { + reg_in = iris_cfg->in_ratio << IRIS_PWIL_IN_FRAME_SHIFT | (1 << 15); + reg_out = iris_cfg->out_ratio << IRIS_PWIL_OUT_FRAME_SHIFT; + cap_enable = (iris_cfg->repeat != IRIS_REPEAT_CAPDIS); + mutex_lock(&iris_cfg->cmd_mutex); + if (frc_repeat_enter) { + frc_repeat_enter = false; + iris_reg_add(FRCC_CTRL_REG8_ADDR, val_frcc_reg8); + iris_reg_add(FRCC_CTRL_REG16_ADDR, val_frcc_reg16); + iris_reg_add(FRCC_CMD_MOD_TH, val_frcc_cmd_th); + iris_reg_add(FRCC_REG_SHOW, 0x2); + //if (!debug_hlmd_enabled) + if (!iris_cfg->true_cut_enable) + { + iris_reg_add(IRIS_PWIL_ADDR + 0x12FC, reg_in); + iris_reg_add(IRIS_PWIL_ADDR + 0x0638, reg_out); + iris_reg_add(IRIS_PWIL_ADDR + 0x10000, (1 << 8) | (1 << 6)); + } + } + mutex_unlock(&iris_cfg->cmd_mutex); + } + + pr_debug("vts %u pvts %u cap_en %d\n", iris_cfg->meta.video_ts, iris_cfg->prev_vts, cap_enable); + + return cap_enable; +} + +void iris_cmd_cadence_check(struct mdss_mdp_ctl *ctl) +{ + static u32 prev_frame_addr; + static u32 prev_frame_count, prev_frames; + static enum { C11 = 11, C22 = 22, C32 = 32 } cadence = C11; + static int badedit_cnt; + bool bad = false; + + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(ctl->mfd); + struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); + struct mdss_mdp_pipe *pipe; + struct mdss_mdp_mixer *mixer; + u32 frame_addr, frame_count, frames; + + if (!mdp5_data && !sctl) + return; + list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { + if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) + goto check_cadence; + } + return; + +check_cadence: + mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); + if (!mixer) + return; + + frame_count = (mdss_mdp_pingpong_read(mixer->pingpong_base, MDSS_MDP_REG_PP_INT_COUNT_VAL) >> 16) & 0xffff; + frames = frame_count - prev_frame_count; + frame_addr = readl(pipe->base + MDSS_MDP_REG_SSPP_SRC0_ADDR); + pr_debug("=== frame %08x count %u diff %u\n", + frame_addr, frame_count, frames); + if (frame_addr == prev_frame_addr) + return; + + switch (cadence) { + case C11: + if (frames == 2 && prev_frames == 2) + cadence = C22; + else if (frames == 3 && prev_frames == 2) + cadence = C32; + else if (frames == 2 && prev_frames == 3) + cadence = C32; + break; + case C22: + if (frames != 2) + bad = true; + break; + case C32: + if (!((frames == 3 && prev_frames == 2) || + (frames == 2 && prev_frames == 3))) + bad = true; + break; + } + if (bad) { + badedit_cnt++; + //trace_exynos_busfreq_target_mif(badedit_cnt); + pr_debug("=== bad edit %d === (cadence %u, frames %u)\n", + badedit_cnt, cadence, frames); + cadence = C11; + } + + prev_frame_addr = frame_addr; + prev_frame_count = frame_count; + prev_frames = frames; +} + +#if 0 +static int check_mode_status(struct mdss_dsi_ctrl_pdata *ctrl, int mode) +{ + int i; + u32 val = 0; + int try_cnt = 10; + int ret = 0; + + if (!debug_mode_switch_enabled) + return ret; + + for (i = 0; i < try_cnt; i++) { + msleep(16); +#if defined(FPGA_PLATFORM) + if (i == try_cnt - 1) + val = IRIS_FRC_MODE; +#else + val = iris_pi_read(ctrl, IRIS_MODE_ADDR); +#endif + if (val == mode) + break; + else + pr_err("%s:%d: %08x, cnt = %d\n", __func__, __LINE__, val, i); + + } + + if (i == try_cnt) { + pr_err("%s: check mode (%d) error\n", __func__, mode); + ret = -1; + } + return ret; +} +#endif + +static void iris_pt_entry_wq_handler(struct work_struct *work) +{ + + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + + pt_enable[0] = 0x0; + pt_enable[1] = 0x1; + + mdss_dsi_cmds_tx(ctrl, pt_mode_enter, + ARRAY_SIZE(pt_mode_enter), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_rfb_entry_wq_handler(struct work_struct *work) +{ + + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + + pt_enable[0] = 0x3 << 2; + pt_enable[1] = 0x1; + + mdss_dsi_cmds_tx(ctrl, pt_mode_enter, + ARRAY_SIZE(pt_mode_enter), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + mdss_dsi_cmds_tx(ctrl, rfb_data_path_config, + ARRAY_SIZE(rfb_data_path_config), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + // TODO: use 200ms to instead the read command + usleep_range(200000, 200000); + + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_pt_prepare_handler(struct work_struct *work) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct mdss_panel_info *pinfo = g_mfd->panel_info; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + BUG_ON(ctrl == NULL || pinfo == NULL); + + if (g_mfd->panel_info->type == MIPI_VIDEO_PANEL) + { + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + mdss_dsi_cmds_tx(ctrl, pt_data_path_config, + ARRAY_SIZE(pt_data_path_config), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + // TODO: use 200ms to instead the read command + usleep_range(200000, 200000); + } + + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_rfb_prepare_handler(struct work_struct *work) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct mdss_panel_info *pinfo = g_mfd->panel_info; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + BUG_ON(ctrl == NULL || pinfo == NULL); + + if (g_mfd->panel_info->type == MIPI_VIDEO_PANEL) + { + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + mdss_dsi_cmds_tx(ctrl, rfb_data_path_config, + ARRAY_SIZE(rfb_data_path_config), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + // TODO: use 200ms to instead the read command + usleep_range(200000, 200000); + } + + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_memc_prepare_handler(struct work_struct *work) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct mdss_panel_info *pinfo = g_mfd->panel_info; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + BUG_ON(ctrl == NULL || pinfo == NULL); + + if (g_mfd->panel_info->type == MIPI_VIDEO_PANEL) + { + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + mdss_dsi_cmds_tx(ctrl, memc_data_path_config, + ARRAY_SIZE(memc_data_path_config), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + // TODO: use 200ms to instead the read command + usleep_range(200000, 200000); + } + + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_memc_cancel_handler(struct work_struct *work) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + BUG_ON(ctrl == NULL || iris_cfg == NULL); +//todos, embedded not ready. +#if 0 + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + mdss_dsi_cmds_tx(ctrl, memc_cancel, + ARRAY_SIZE(memc_cancel)); + + check_mode_status(ctrl, IRIS_PT_MODE); +#endif + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +static void iris_memc_entry_handler(struct work_struct *work) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct mdss_panel_info *pinfo = g_mfd->panel_info; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + BUG_ON(ctrl == NULL || pinfo == NULL); + iris_proc_frcc_setting(g_mfd); + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + mdss_dsi_cmds_tx(ctrl, memc_mode_enter, + ARRAY_SIZE(memc_mode_enter), (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + atomic_dec(&iris_cfg->mode_switch_cnt); + pr_debug("%s ------\n", __func__); +} + +void iris_regs_clear(void) +{ + iris_reg_cnt = 0; + //memset(iris_regs, 0, sizeof(iris_regs)); +} + +void iris_reg_add(u32 addr, u32 val) +{ + if (iris_reg_cnt >= IRIS_REGS) { + pr_warn("iris reg add count is overflow.\n"); + return; + } + pr_debug("regs[%i:%08x] = %08x\n", iris_reg_cnt, addr, val); + iris_regs[iris_reg_cnt].addr = addr; + iris_regs[iris_reg_cnt].val = val; + iris_reg_cnt++; +} + +static int iris_regs_meta_build(void) +{ + int i; + int size; + char imeta_header[META_HEADER] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x3), + 0x00, + 0x00, + PWIL_U16(0x2), + }; + + pr_debug("reg_cnt: %02x", iris_reg_cnt); + memcpy(imeta, imeta_header, META_HEADER); + // pair + for (i = 0; i < iris_reg_cnt; i++) { + *(u32 *)(imeta + META_HEADER + i*8) = cpu_to_le32(iris_regs[i].addr); + *(u32 *)(imeta + META_HEADER + i*8 + 4) = cpu_to_le32(iris_regs[i].val); + /* + imeta[META_HEADER + i*8 ] = iris_regs[i].addr & 0xff; + imeta[META_HEADER + i*8 + 1] = (iris_regs[i].addr >> 8) & 0xff; + imeta[META_HEADER + i*8 + 2] = (iris_regs[i].addr >> 16) & 0xff; + imeta[META_HEADER + i*8 + 3] = (iris_regs[i].addr >> 24) & 0xff; + + imeta[META_HEADER + i*8 + 4] = iris_regs[i].addr & 0xff; + imeta[META_HEADER + i*8 + 5] = (iris_regs[i].addr >> 8) & 0xff; + imeta[META_HEADER + i*8 + 6] = (iris_regs[i].addr >> 16) & 0xff; + imeta[META_HEADER + i*8 + 7] = (iris_regs[i].addr >> 24) & 0xff; + */ + } + // size update + size = iris_reg_cnt * 2; + *(u32 *)(imeta + 8) = cpu_to_le32(size + 1); + *(u16 *)(imeta + 14) = cpu_to_le16(size); + iris_meta_pkts[0].dchdr.dlen = META_HEADER + iris_reg_cnt * 8; + return iris_reg_cnt; +} + +void iris_copy_meta(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + // copy meta + mutex_lock(&iris_cfg->meta_mutex); + if (iris_cfg->meta_set.op) { + memcpy((void *)&iris_cfg->meta, (void *)&iris_cfg->meta_set, sizeof(struct iris_meta)); + memset((void *)&iris_cfg->meta_set, 0, sizeof(struct iris_meta)); + pr_debug("iris_copy_meta\n"); + } + mutex_unlock(&iris_cfg->meta_mutex); +} + + +void iris_i2c_send_meta(struct mdss_mdp_ctl *ctl) +{ + int i = 0; + int len = 0; + + struct iris_config *iris_cfg = &g_mfd->iris_conf; + struct addr_val addr_list[IRIS_REGS]; + + if (!debug_send_meta_enabled) + return; + + if (ctl && ! mdss_mdp_ctl_is_power_on(ctl)) { + pr_err("power is off\n"); + return; + } + + if ((atomic_read(&g_mfd->iris_conf.mode_switch_cnt))) + return; + + if (iris_cfg->cap_change) { + //cadence change , capture disable and capture enable + if ( iris_cfg->cap_enable ) { + iris_reg_add(IRIS_PWIL_ADDR +0x1204, 0x00020001); + } else { + iris_reg_add(IRIS_PWIL_ADDR +0x1204, 0x00100001); + } + iris_cfg->cap_change = false; + pr_debug("cap_change: %d\n", iris_cfg->cap_enable); + } + + if (iris_reg_cnt <= 0) { + //mutex_lock(&g_mfd->iris_conf.cmd_mutex); + //if (!iris_regs_meta_build()) { + //mutex_unlock(&g_mfd->iris_conf.cmd_mutex); + return; + } + memset(addr_list , 0x00, sizeof(addr_list)); + + for (i = 0; i < iris_reg_cnt; i++) { + addr_list[i].addr = cpu_to_le32(iris_regs[i].addr); + addr_list[i].data = cpu_to_le32(iris_regs[i].val); + } + len = iris_reg_cnt; + iris_regs_clear(); + //mutex_unlock(&g_mfd->iris_conf.cmd_mutex); + iris_i2c_write(addr_list, len); +} + + +void iris_send_meta_cmd(struct mdss_mdp_ctl *ctl) +{ + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct dcs_cmd_req cmdreq; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + int cmd; + + BUG_ON(ctrl == NULL); + + if (!debug_send_meta_enabled) + return; + + if ((atomic_read(&g_mfd->iris_conf.mode_switch_cnt))) + return; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_HS_MODE | CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + if (iris_cfg->cap_change) { + static char cursor_enable[2] = {0x04, 0x10}; + static struct dsi_cmd_desc cursor_mode_enter[] = { + { { DTYPE_GEN_WRITE2, 1, 0, 0, 0, sizeof(cursor_enable) }, cursor_enable},}; + if (iris_cfg->cap_enable) + cmdreq.cmds = memc_mode_enter; + else + cmdreq.cmds = cursor_mode_enter; + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + iris_cfg->cap_change = false; + pr_debug("cap_change: %d\n", iris_cfg->cap_enable); + } + + pr_debug("%s ++++++\n", __func__); + mutex_lock(&g_mfd->iris_conf.cmd_mutex); + if (!iris_regs_meta_build()) { + mutex_unlock(&g_mfd->iris_conf.cmd_mutex); + return; + } + iris_regs_clear(); + mutex_unlock(&g_mfd->iris_conf.cmd_mutex); + cmdreq.cmds = iris_meta_pkts; + iris_meta_pkts[0].dchdr.last = 1; + + for (cmd = 0; cmd < cmdreq.cmds_cnt; cmd++) { + pr_debug("dchdr: %02x %02x %02x %02x %02x %02x\n", + iris_meta_pkts[cmd].dchdr.dtype, + iris_meta_pkts[cmd].dchdr.last, + iris_meta_pkts[cmd].dchdr.vc, + iris_meta_pkts[cmd].dchdr.ack, + iris_meta_pkts[cmd].dchdr.wait, + iris_meta_pkts[cmd].dchdr.dlen); + { + int i; + for (i = 0; i < iris_meta_pkts[cmd].dchdr.dlen; i += 8) + pr_debug("%02x %02x %02x %02x %02x %02x %02x %02x\n", + iris_meta_pkts[cmd].payload[i], iris_meta_pkts[cmd].payload[i+1], + iris_meta_pkts[cmd].payload[i+2], iris_meta_pkts[cmd].payload[i+3], + iris_meta_pkts[cmd].payload[i+4], iris_meta_pkts[cmd].payload[i+5], + iris_meta_pkts[cmd].payload[i+6], iris_meta_pkts[cmd].payload[i+7]); + } + } + + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + //memset(imeta, 0, sizeof(imeta)); +} + +static void iris_meta_wq_handler(void) +{ + struct mdss_mdp_ctl *ctl = mfd_to_ctl(g_mfd); + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct mdss_panel_data *pdata = ctl->panel_data; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + int cmd; + int cnt; + + if (g_mfd->panel_info->type == MIPI_VIDEO_PANEL) + { + cnt = 1; + } + else + { + cnt = 2; + } + + //if (ctl->power_state == MDSS_PANEL_POWER_OFF) + // return; + if (pdata->panel_info.panel_power_state == MDSS_PANEL_POWER_OFF) + return; + pr_debug("%s ++++++\n", __func__); + mutex_lock(&iris_cfg->cmd_mutex); + iris_regs_meta_build(); + iris_regs_clear(); + mutex_unlock(&iris_cfg->cmd_mutex); + // TODO: when okay use ioctl or other side band to enable new frame + iris_meta_pkts[0].dchdr.last = debug_new_frame_enabled ? 0 : 1; + iris_meta_pkts[1].dchdr.last = debug_new_frame_enabled ? 1 : 0; + if (g_mfd->panel_info->type == MIPI_VIDEO_PANEL) + { + iris_meta_pkts[0].dchdr.last = 1; + } + for (cmd = 0; cmd < cnt; cmd++) { + pr_debug("dchdr: %02x %02x %02x %02x %02x %02x\n", + iris_meta_pkts[cmd].dchdr.dtype, + iris_meta_pkts[cmd].dchdr.last, + iris_meta_pkts[cmd].dchdr.vc, + iris_meta_pkts[cmd].dchdr.ack, + iris_meta_pkts[cmd].dchdr.wait, + iris_meta_pkts[cmd].dchdr.dlen); + { + int i; + for (i = 0; i < iris_meta_pkts[cmd].dchdr.dlen; i += 8) + pr_debug("%02x %02x %02x %02x %02x %02x %02x %02x\n", + iris_meta_pkts[cmd].payload[i], iris_meta_pkts[cmd].payload[i+1], + iris_meta_pkts[cmd].payload[i+2], iris_meta_pkts[cmd].payload[i+3], + iris_meta_pkts[cmd].payload[i+4], iris_meta_pkts[cmd].payload[i+5], + iris_meta_pkts[cmd].payload[i+6], iris_meta_pkts[cmd].payload[i+7]); + } + } + mdss_dsi_cmd_hs_mode(1, &ctrl->panel_data); + // TODO: assume 2 meta packet will both issued at same kickoff + if (iris_meta_pkts[0].dchdr.dlen > META_HEADER) + mdss_dsi_cmds_tx(ctrl, iris_meta_pkts, cnt, (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + memset(imeta, 0, sizeof(imeta)); + pr_debug("%s ------\n", __func__); +} + + +void iris_send_meta_video(struct mdss_mdp_ctl *ctl) +{ + struct msm_fb_data_type *mfd = ctl->mfd; + struct iris_config *iris_cfg = &mfd->iris_conf; + + BUG_ON(iris_cfg == NULL); + + if (!debug_send_meta_enabled) + return; + + if ((atomic_read(&g_mfd->iris_conf.mode_switch_cnt))) + return; + + //schedule_work(&iris_cfg->meta_work); + iris_mgmt.iris_handler = iris_meta_wq_handler; + queue_work(iris_mgmt.iris_wq, &iris_mgmt.iris_worker); +} + + +// shall be called before params_changed clear to 0 +static int iris_proc_repeat(struct iris_config *iris_cfg) +{ + u8 prev_repeat; + int ret; + + prev_repeat = iris_cfg->repeat; + + if (debug_repeat_enabled > 3) { + iris_cfg->repeat = debug_repeat_enabled - 3; + } else { + iris_cfg->repeat = (iris_cfg->meta.op & MDP_IRIS_OP_RPT) ? iris_cfg->meta.repeat : iris_cfg->repeat; + } + + pr_debug("repeat = %d\n", iris_cfg->repeat); + + ret = ((iris_cfg->repeat != prev_repeat) || (iris_cfg->repeat == IRIS_REPEAT_FORCE)); + return ret; +} + +static int iris_set_ratio(struct iris_config *iris_cfg) +{ + unsigned int reg_in, reg_out, reg_scale; + bool cap_enable; + + reg_in = iris_cfg->in_ratio << IRIS_PWIL_IN_FRAME_SHIFT | (1 << 15); + reg_out = iris_cfg->out_ratio << IRIS_PWIL_OUT_FRAME_SHIFT; + reg_scale = 4096/iris_cfg->scale << 24 | 64 << 16 | iris_cfg->scale << 8 | iris_cfg->scale; + /* duplicated video frame */ + cap_enable = iris_cfg->repeat != IRIS_REPEAT_CAPDIS; + /*set ratio after mode switch to FRC */ + if (!debug_ratio_enabled || + ((iris_cfg->sf_notify_mode != IRIS_MODE_FRC) && + (iris_cfg->sf_notify_mode != IRIS_MODE_RFB2FRC))) // FIXME, whether set when FRC_PREPARE + return true; + + if (iris_cfg->true_cut_enable) + return cap_enable; + + pr_debug("vts %u pvts %u cap_enable %d\n", iris_cfg->meta.video_ts, iris_cfg->prev_vts, cap_enable); + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x12FC, reg_in); + iris_reg_add(IRIS_PWIL_ADDR + 0x0638, reg_out); + iris_reg_add(IRIS_PWIL_ADDR + 0x10000, (1 << 8) | (1 << 6)); + iris_reg_add(IRIS_MVC_ADDR + 0x1D0, reg_scale); + iris_reg_add(IRIS_MVC_ADDR + 0x1FF00, 1); + mutex_unlock(&iris_cfg->cmd_mutex); + return cap_enable; +} + +bool iris_frc_repeat(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret_r, ret_p; + int cap_enable = true; + bool ret; + + if (iris_cfg->sf_notify_mode != IRIS_MODE_FRC) + return cap_enable; + + ret_r = iris_proc_ratio(iris_cfg); + ret_p = iris_proc_repeat(iris_cfg); + if (ret_p) + cap_enable = iris_set_repeat(iris_cfg); + else if (ret_r) + cap_enable = iris_set_ratio(iris_cfg); + else { + cap_enable = iris_cfg->cap_enable; + pr_debug("keep the last value: %d!\n", cap_enable); + } + + if (iris_cfg->sf_notify_mode == IRIS_MODE_FRC) { + if (cap_enable != iris_cfg->cap_enable) { + pr_debug("capture-change: %d!\n", cap_enable); + if (debug_new_repeat == 1) + iris_cfg->cap_change = true; + else if (debug_new_repeat == 0) { + unsigned int reg_cap; + if (cap_enable) + reg_cap = 0xc0000003; + else + reg_cap = 0xc0000001; + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x0218, reg_cap); + mutex_unlock(&iris_cfg->cmd_mutex); + } + iris_cfg->cap_enable = cap_enable; + } + } + + ret = ((debug_new_repeat == 2) ? cap_enable : true); + return ret; +} + + +int iris_proc_frcc_setting(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_panel_data *pdata = mdp5_data->ctl->panel_data; + struct quality_setting *pqlt_cur_setting = &iris_info.setting_info.quality_cur; + + // default val of reference register which need host to set. + u32 val_frcc_reg5 = 0x3c010000; + u32 val_frcc_reg8 = 0x10000000; + u32 val_frcc_reg16 = 0x413120c8; + u32 val_frcc_reg17 = 0x8000; + u32 val_frcc_reg18 = 0; + u32 val_frcc_cmd_th = 0x8000; + u32 val_frcc_dtg_sync = 0; + + //formula variable + u32 ThreeCoreEn, VD_CAP_DLY1_EN; + u32 MaxFIFOFI, KeepTH, CarryTH, RepeatP1_TH; + u32 RepeatCF_TH, TS_FRC_EN, INPUT_RECORD_THR, MERAREC_THR_VALID; + u32 MetaGen_TH1, MetaGen_TH2, MetaRec_TH1, MetaRec_TH2; + u32 FIRepeatCF_TH; + + //timing and feature variable + u32 te_fps, display_vsync, Input_Vres, Scaler_EN = false, Capture_EN, Input_Vtotal; + u32 DisplayVtotal, HsyncFreqIn, HsyncFreqOut, InVactive, StartLine, Vsize; + int inputwidth = (iris_info.work_mode.rx_ch ? iris_info.input_timing.hres * 2 : iris_info.input_timing.hres); + u32 Infps = iris_cfg->input_frame_rate; + int adjustmemclevel = 3; + int hlmd_func_enable = 0; + + //init variable + te_fps = mdss_panel_get_framerate(&pdata->panel_info); + display_vsync = 60;//iris to panel, TODO, or 120 + Input_Vres = pdata->panel_info.yres; +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + Capture_EN = iris_cfg->nrv_enable | iris_cfg->drc_enable; +#else + Capture_EN = iris_cfg->nrv_enable; +#endif + Input_Vtotal = mdss_panel_get_vtotal(&pdata->panel_info); + if (lp_memc_timing[0] != inputwidth) + Scaler_EN = true; + else + Scaler_EN = false; + DisplayVtotal = iris_get_vtotal(&iris_info.output_timing); + HsyncFreqIn = te_fps * Input_Vtotal; + HsyncFreqOut = display_vsync * DisplayVtotal; + InVactive = iris_cfg->meta.nrv.captureBottom - iris_cfg->meta.nrv.captureTop; +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + if (iris_cfg->drc_enable) + InVactive = (iris_cfg->drc_size >> 16); +#endif + if (Capture_EN) + StartLine = Input_Vres - InVactive; + else if (Scaler_EN) + StartLine = 5; + else + StartLine = 0; + if (Capture_EN) + Vsize = InVactive; + else + Vsize = Input_Vtotal;//DisplayVtotal; + + pr_debug("%s: get timing info, infps=%d, displayVtotal = %d, InVactive = %d, StartLine = %d, Vsize = %d\n", + __func__, Infps, DisplayVtotal, InVactive, StartLine, Vsize); + pr_debug("TE_fps = %d, display_vsync = %d, inputVres = %d, Scaler_EN = %d, capture_en = %d, InputVtotal = %d\n", + te_fps, display_vsync, Input_Vres, Scaler_EN, Capture_EN, Input_Vtotal); + + if (mfd->panel_info->type == MIPI_VIDEO_PANEL) { + //video mode + ThreeCoreEn = 1; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 3; KeepTH = 252; CarryTH = 5; + RepeatP1_TH = 5; RepeatCF_TH = 252; TS_FRC_EN = 0; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + FIRepeatCF_TH = 252; + goto VAL_CALC; + } + + if (iris_cfg->fbo_enable) { + //TODO mbo mode + ThreeCoreEn = 1; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 4; KeepTH = 252; CarryTH = 5; + RepeatP1_TH = 5; RepeatCF_TH = 252; TS_FRC_EN = 0; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + FIRepeatCF_TH = 252; + goto VAL_CALC; + } + + pr_debug("iris_cfg->input_vfr: %d", iris_cfg->input_vfr); + //check input is variable frame rate or not. + switch (iris_cfg->input_vfr) { + case 15:// 15 fps from 24/25 fps. + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 5; KeepTH = 253; CarryTH = 2; + RepeatP1_TH = 2; RepeatCF_TH = 253; TS_FRC_EN = 1; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + if (debug_hlmd_enabled && !iris_cfg->true_cut_enable) { + hlmd_func_enable = 1; + RepeatP1_TH = 1; + CarryTH = 1; + } else { + hlmd_func_enable = 0; + } + goto VAL_CALC; + case 50:// vfr from 50 drop + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 4; KeepTH = 253; CarryTH = 2; + RepeatP1_TH = 2; RepeatCF_TH = 253; TS_FRC_EN = 1; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + goto VAL_CALC; + case 60:// vfr from 60 drop + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 4; KeepTH = 253; CarryTH = 1; + RepeatP1_TH = 1; RepeatCF_TH = 253; TS_FRC_EN = 1; MERAREC_THR_VALID = 0; + MetaGen_TH1 = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + goto VAL_CALC; + case 0:// vfr is invalid, frame rate is constant + default : + break; + } + + switch (Infps) { + case 24://24fps + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 3; KeepTH = 252; CarryTH = 5; + RepeatP1_TH = 5; RepeatCF_TH = 252; TS_FRC_EN = 0; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + FIRepeatCF_TH = 252; + break; + case 30://30fps + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 2; KeepTH = 252; CarryTH = 5; + RepeatP1_TH = 5; RepeatCF_TH = 252; TS_FRC_EN = 0; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + FIRepeatCF_TH = 252; + break; + case 25://25fps + ThreeCoreEn = 1; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 3; KeepTH = 253; CarryTH = 2; + RepeatP1_TH = 2; RepeatCF_TH = 253; TS_FRC_EN = 0; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 3 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + break; + case 15://15fps + if (debug_hlmd_enabled && !iris_cfg->true_cut_enable) { + hlmd_func_enable = 1; + RepeatP1_TH = 1; + CarryTH = 1; + } else { + hlmd_func_enable = 0; + RepeatP1_TH = 2; + CarryTH = 2; + } + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 5; KeepTH = 253; + RepeatCF_TH = 253; TS_FRC_EN = 1; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + case 12://12fps + ThreeCoreEn = 0; VD_CAP_DLY1_EN = 0; MaxFIFOFI = 5; KeepTH = 253; CarryTH = 2; + RepeatP1_TH = 2; RepeatCF_TH = 253; TS_FRC_EN = 1; MERAREC_THR_VALID = 1; + MetaGen_TH1 = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaGen_TH2 = (Vsize * 6 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH1 = (Vsize * 5 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + MetaRec_TH2 = (Vsize * 7 / 8 + StartLine) * HsyncFreqOut / HsyncFreqIn; + INPUT_RECORD_THR = (Vsize / 2 + StartLine) * HsyncFreqOut / HsyncFreqIn - 10; + FIRepeatCF_TH = 253; + break; + default: + pr_err("%s, using default frcc parameters\n", __func__); + goto SET_REG; + } + +VAL_CALC: + if (pqlt_cur_setting->pq_setting.memclevel == 3) + adjustmemclevel = 3; + else if (pqlt_cur_setting->pq_setting.memclevel == 2) + adjustmemclevel = 3; + else if (pqlt_cur_setting->pq_setting.memclevel == 1) + adjustmemclevel = 2; + else if (pqlt_cur_setting->pq_setting.memclevel == 0) + adjustmemclevel = 0; + + //val_frcc_reg5 = val_frcc_reg5 + ((pqlt_cur_setting->pq_setting.memclevel & 0x3) << 17) + (KeepTH * 2 << 7) + CarryTH; + val_frcc_reg5 = val_frcc_reg5 + ((adjustmemclevel & 0x3) << 17) + (KeepTH << 8) + CarryTH; + val_frcc_reg8 = val_frcc_reg8 + (RepeatP1_TH << 8) + RepeatCF_TH; + val_frcc_reg16 = val_frcc_reg16 + (TS_FRC_EN << 31) + (ThreeCoreEn << 15) + VD_CAP_DLY1_EN; + val_frcc_reg17 = val_frcc_reg17 + (DisplayVtotal << 16) + INPUT_RECORD_THR; + val_frcc_reg18 = val_frcc_reg18 + (MERAREC_THR_VALID << 31) + (MetaRec_TH2 << 16) + MetaRec_TH1; + val_frcc_cmd_th = val_frcc_cmd_th + (MaxFIFOFI << 29) + (MetaGen_TH2 << 16) + MetaGen_TH1; + val_frcc_dtg_sync |= FIRepeatCF_TH << FI_REPEATCF_TH; + +SET_REG: + pr_debug("%s: reg5=%x, reg8=%x, reg16=%x, reg17=%x, reg18=%x, cmd_th=%x\n", __func__, + val_frcc_reg5, val_frcc_reg8, val_frcc_reg16, val_frcc_reg17, val_frcc_reg18, val_frcc_cmd_th); + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(FRCC_CTRL_REG5_ADDR, val_frcc_reg5); + iris_reg_add(FRCC_CTRL_REG8_ADDR, val_frcc_reg8); + iris_reg_add(FRCC_CTRL_REG16_ADDR, val_frcc_reg16); + iris_reg_add(FRCC_CTRL_REG17_ADDR, val_frcc_reg17); + iris_reg_add(FRCC_CTRL_REG18_ADDR, val_frcc_reg18); + iris_reg_add(FRCC_DTG_SYNC, val_frcc_dtg_sync); + iris_reg_add(FRCC_CMD_MOD_TH, val_frcc_cmd_th); + iris_reg_add(FRCC_REG_SHOW, 0x2); + if (debug_hlmd_enabled) { + if (hlmd_func_enable) + iris_reg_add(IRIS_MVC_ADDR + 0x1ffe8, 0x00200000); + } + if (iris_cfg->true_cut_enable) { + iris_reg_add(IRIS_MVC_ADDR + 0x1ffe8, 0x00200000); + } else { + iris_reg_add(IRIS_MVC_ADDR + 0x1ffe8, 0x00000000); + } + mutex_unlock(&iris_cfg->cmd_mutex); + iris_cfg->val_frcc_cmd_th = val_frcc_cmd_th; + iris_cfg->val_frcc_reg8 = val_frcc_reg8; + iris_cfg->val_frcc_reg16 = val_frcc_reg16; + return 0; +} + +void mdss_dsi_iris_init(struct msm_fb_data_type *mfd) +{ + printk("###%s:%d: mfd->panel.type: %i mfd->panel.id: %i\n", __func__, __LINE__, mfd->panel.type, mfd->panel.id); + if (mfd->index != 0) + return; + if (!(mfd->panel.type == MIPI_VIDEO_PANEL || mfd->panel.type == MIPI_CMD_PANEL)) + return; + + g_mfd = mfd; + printk("###%s:%d: g_mfd: %p\n", __func__, __LINE__, g_mfd); + iris_mgmt.iris_wq = create_singlethread_workqueue("iris_wq"); + INIT_WORK(&iris_mgmt.iris_worker, iris_cmds_tx); + + mfd->iris_conf.current_mode = IRIS_RFB_MODE; + mfd->iris_conf.sf_notify_mode = IRIS_MODE_RFB; + mfd->iris_conf.fbo_enable = false; + mfd->iris_conf.memc_enable = false; + mfd->iris_conf.mode_switch_finish = true; + mfd->iris_conf.sf_mode_change_start = false; + mfd->iris_conf.repeat = IRIS_REPEAT_NO; + mfd->iris_conf.video_on = false; + mfd->iris_conf.rfb_path = IRIS_RFB_DATA_PATH_DEFAULT; + mfd->iris_conf.pt_path = IRIS_PT_DATA_PATH_DEFAULT; + mfd->iris_conf.frc_path = IRIS_FRC_DATA_PATH_DEFAULT; + atomic_set(&mfd->iris_conf.mode_switch_cnt, 0); + mfd->iris_conf.input_frame_rate = 60; + mfd->iris_conf.output_frame_rate = 60; + mfd->iris_conf.input_vfr = 0; + mfd->iris_conf.in_ratio = 1; + mfd->iris_conf.out_ratio = 1; + mfd->iris_conf.vp_continous = 0; + mfd->iris_conf.nrv_enable = false; + mfd->iris_conf.true_cut_enable = false; + mfd->iris_conf.ready = false; + mfd->iris_conf.prev_dvts = 0; + mfd->iris_conf.iris_ratio_updated = false; + mfd->iris_conf.cap_change = false; + mfd->iris_conf.tx_switch_state = IRIS_TX_SWITCH_NONE; + mfd->iris_conf.tx_switch_debug_flag = 0; + mfd->iris_conf.lp_frc_timing = 0; +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + mfd->iris_conf.drc_enable = false; + mfd->iris_conf.drc_size = 0; +#endif + memset((void *)&mfd->iris_conf.meta, 0, sizeof(struct iris_meta)); + memset((void *)&mfd->iris_conf.meta_set, 0, sizeof(struct iris_meta)); + memset((void *)&mfd->iris_conf.avail_mode, 0, sizeof(struct iris_available_mode)); + mfd->iris_conf.avail_mode.prefer_mode = IRIS_MODE_RFB; + mfd->iris_conf.avail_mode.pt_threshold_low = 0; + mfd->iris_conf.avail_mode.rfb_threshold_low = 0; + mfd->iris_conf.avail_mode.rfb_threshold_high = 0; + mfd->iris_conf.avail_mode.pt_threshold_high = 0; + if (mfd->panel.type == MIPI_VIDEO_PANEL) { + mfd->iris_conf.avail_mode.dsi_mode_in_rfb = DSI_VIDEO_MODE; + mfd->iris_conf.avail_mode.dsi_mode_in_ptl = DSI_VIDEO_MODE; + mfd->iris_conf.avail_mode.dsi_mode_in_pth = DSI_VIDEO_MODE; + } else if (mfd->panel.type == MIPI_CMD_PANEL) { + mfd->iris_conf.avail_mode.dsi_mode_in_rfb = DSI_CMD_MODE; + mfd->iris_conf.avail_mode.dsi_mode_in_ptl = DSI_CMD_MODE; + mfd->iris_conf.avail_mode.dsi_mode_in_pth = DSI_CMD_MODE; + } + spin_lock_init(&mfd->iris_conf.iris_reset_lock); + iris_regs_clear(); + INIT_WORK(&mfd->iris_conf.pt_work, iris_pt_entry_wq_handler); + INIT_WORK(&mfd->iris_conf.rfb_work, iris_rfb_entry_wq_handler); + INIT_WORK(&mfd->iris_conf.pt_prepare_work, iris_pt_prepare_handler); + INIT_WORK(&mfd->iris_conf.rfb_prepare_work, iris_rfb_prepare_handler); + INIT_WORK(&mfd->iris_conf.memc_work, iris_memc_entry_handler); + INIT_WORK(&mfd->iris_conf.memc_prepare_work, iris_memc_prepare_handler); + INIT_WORK(&mfd->iris_conf.memc_cancel_work, iris_memc_cancel_handler); + mutex_init(&mfd->iris_conf.cmd_mutex); + mutex_init(&mfd->iris_conf.config_mutex); + mutex_init(&mfd->iris_conf.meta_mutex); + + iris_debugfs_init(mfd); + iris_i2c_bus_init(); +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p.h new file mode 100644 index 000000000000..f3967a768c7a --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p.h @@ -0,0 +1,296 @@ +/* Copyright (c) 2013, Pixelworks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef MDSS_DSI_IRIS_H +#define MDSS_DSI_IRIS_H + +#include "mdss_mdp.h" +#include "mdss_dsi.h" +#include "linux/fb.h" +#include +#include "mdss_dsi_iris2p_lightup_priv.h" + +/* WITHOUT_IRIS means that panel is connected to host directly, default is disabled. */ +//#define WITHOUT_IRIS +//#define FPGA_PLATFORM + + +#define IRIS_PSR_MIF_ADDR 0xF1400000 +#define IRIS_BLENDING_ADDR 0xF1540000 +#define IRIS_GMD_ADDR 0xF20A0000 +#define IRIS_FBD_ADDR 0xF20C0000 +#define IRIS_CAD_ADDR 0xF20E0000 +#define FRCC_CTRL_REG5_ADDR 0xF2010014 +#define FRCC_CTRL_REG7_ADDR 0xF201001C +#define FRCC_CTRL_REG8_ADDR 0xF2010020 +#define FRCC_CTRL_REG16_ADDR 0xF2010040 +#define FRCC_CTRL_REG17_ADDR 0xF2010044 +#define FRCC_CTRL_REG18_ADDR 0xF2010048 +#define FRCC_CMD_MOD_TH 0xF201004c +#define FRCC_DTG_SYNC 0xF2010060 +#define DTG_SYNC_TH 0 +#define TH_DTG_EN 15 +#define FI_REPEATCF_TH 16 +#define VD_P1_CHECK_EN 26 +#define MAX_TRSC_DONE 27 +#define DELAY_FI_EN 29 +#define FRCC_REG_SHOW 0xF2011198 +#if !defined(MIPI_SWAP) +#define IRIS_MIPI_RX2_ADDR 0xF0140000 +#define IRIS_MIPI_TX2_ADDR 0xF01c0000 +#endif +#define PWIL_STATUS_ADDR 0xF1240080 +#define IRIS_PWIL_OUT_FRAME_SHIFT 24 +#define IRIS_PWIL_IN_FRAME_SHIFT 8 +#define IRIS_MVC_ADDR 0xF2100000 +#define IRIS_MVC_TOP_CTRL0_OFF 0x0000000c +#define IRIS_MVC_SW_UPDATE_OFF 0x1ff00 +#define PHASE_SHIFT_EN 1 +#define INTERVAL_SHIFT_EN 2 +#define TRUE_CUT_EXT_EN 3 +#define ROTATION 4 +#define IRIS_BLC_PWM_ADDR 0xF1080000 +#define SCALE 0x1D0 +#define FI_RANGE_CTRL 0xf2160014 +#define FI_DEMO_COL_SIZE 0xf2160018 +#define FI_DEMO_MODE_CTRL 0xf216001c +#define FI_DEMO_MODE_RING 0xf2160020 +#define FI_DEMO_ROW_SIZE 0xf2160024 +#define FI_SHADOW_UPDATE 0xf217ff00 +#define PEAKING_CTRL 0xf1a0005c +#define PEAKING_STARTWIN 0xf1a00060 +#define PEAKING_ENDWIN 0xf1a00064 +#define PEAKING_SHADOW_UPDATE 0xf1a1ff00 +#define CM_CTRL 0xf1560000 +#define CM_STARTWIN 0xf15600dc +#define CM_ENDWIN 0xf15600e0 +#define CM_SHADOW_UPDATE 0xf157ffd0 +#define UNIT_CONTRL_ADDR 0xf0060000 +#define FRC_DSC_ENCODER0 0xf1620000 + +#define IRIS_PROXY_MB0_ADDR (IRIS_PROXY_ADDR+0x00) // MB0 +#define IRIS_MODE_ADDR (IRIS_PROXY_ADDR+0x08) // MB1 +#define IRIS_DATA_PATH_ADDR (IRIS_PROXY_ADDR+0x10) // MB2 +#define IRIS_LPMEMC_SETTING_ADDR (IRIS_PROXY_ADDR+0x20) // MB4 +#define IRIS_DBC_SETTING_ADDR (IRIS_PROXY_ADDR+0x28) // MB5 +#define IRIS_PQ_SETTING_ADDR (IRIS_PROXY_ADDR+0x30) // MB6 + + +#define IRIS_PROXY_MB7_ADDR (IRIS_PROXY_ADDR+0x38) // MB7 + +#define IRIS_COLOR_ADJUST_ADDR (IRIS_GRCP_CTRL_ADDR+0x12fe0) //ALG_PARM11 +#define IRIS_CM_SETTING_ADDR (IRIS_GRCP_CTRL_ADDR+0x12fe4) //ALG_PARM12 +#define IRIS_LCE_SETTING_ADDR (IRIS_GRCP_CTRL_ADDR+0x12fe8) //ALG_PARM13 + + +#define IRIS_TRUECUT_INFO_ADDR (IRIS_GRCP_CTRL_ADDR+0x12fec) +#define IRIS_DRC_INFO_ADDR (IRIS_GRCP_CTRL_ADDR+0x12ff0) +#define IRIS_NRV_INFO1_ADDR (FRC_DSC_ENCODER0+0x0c) // to remove +#define IRIS_NRV_INFO2_ADDR (FRC_DSC_ENCODER0+0x10) // to remove + + +#define PWIL_CTRL_OFFS 0 +#define DPORT_CTRL0_OFFS 0 +#define DPORT_REGSEL 0x1ffd4 + +#define ALIGN_UP(x, size) (((x)+((size)-1))&(~((size)-1))) + +//PWIL View Descriptor Valid Word Number +#define PWIL_ViewD_LEN 0x0A +//PWIL Display Descriptor Valid Word Number +#define PWIL_DispD_LEN 0x05 + +#define IRIS_CONFIGURE_GET_VALUE_CORRECT 0 +#define IRIS_CONFIGURE_GET_VALUE_ERROR 1 + + +#define PWIL_CHECK_FORMAT(cmds) \ + do { \ + int valid_word_num = (ARRAY_SIZE(cmds) - 12) / 4; \ + int non_burst_len = valid_word_num - 1; \ + if (!strncmp(cmds, "LIWP", 4)) { \ + if (!strncmp(cmds + 4, "PCRG", 4)) { \ + cmds[8] = valid_word_num & 0xFF; \ + cmds[9] = (valid_word_num >> 8) & 0xFF; \ + cmds[10] = (valid_word_num >> 16) & 0xFF; \ + cmds[11] = (valid_word_num >> 24) & 0xFF; \ + cmds[14] = non_burst_len & 0xFF; \ + cmds[15] = (non_burst_len >> 8 ) & 0xFF; \ + } else if (!strncmp(cmds + 4, "WEIV", 4)) { \ + cmds[8] = PWIL_ViewD_LEN & 0xFF; \ + cmds[9] = (PWIL_ViewD_LEN >> 8) & 0xFF; \ + cmds[10] = (PWIL_ViewD_LEN >> 16) & 0xFF; \ + cmds[11] = (PWIL_ViewD_LEN >> 24) & 0xFF; \ + } else if (!strncmp(cmds + 4, "PSID", 4)) { \ + cmds[8] = PWIL_DispD_LEN & 0xFF; \ + cmds[9] = (PWIL_DispD_LEN >> 8) & 0xFF; \ + cmds[10] = (PWIL_DispD_LEN >> 16) & 0xFF; \ + cmds[11] = (PWIL_DispD_LEN >> 24) & 0xFF; \ + } else { \ + \ + } \ + } else { \ + pr_err("PWIL Packet format error!\n"); \ + } \ + } while (0) + +enum iris_mode { + IRIS_PT_MODE = 0, + IRIS_RFB_MODE, + IRIS_FRC_MODE, + IRIS_BYPASS_MODE, + IRIS_PT_PRE, + IRIS_RFB_PRE, + IRIS_FRC_PRE, + IRIS_FBO_MODE, +}; + +enum iris_mipi_tx_switch_state { + IRIS_TX_SWITCH_NONE = 0, + IRIS_TX_SWITCH_STEP1, + IRIS_TX_SWITCH_STEP2, + IRIS_TX_SWITCH_STEP3, + IRIS_TX_SWITCH_STEP4, + IRIS_TX_SWITCH_STEP5, + IRIS_TX_SWITCH_INVALID, +}; + +struct iris_mipi_tx_cmd_hdr { + u8 dtype; + u8 len[2]; + u8 ecc; +}; + +union iris_mipi_tx_cmd_header { + struct iris_mipi_tx_cmd_hdr stHdr; + u32 hdr32; +}; + +union iris_mipi_tx_cmd_payload { + u8 p[4]; + u32 pld32; +}; + + +// MB2 +struct iris_fun_enable { + uint32_t reserved0:11; + uint32_t true_cut_en:1; //bit11, Only use in FRC mode + uint32_t reserved1:3; + uint32_t nrv_drc_en:1; //bit15, Only use in FRC mode + uint32_t frc_buf_num:1; //no used + uint32_t phase_en:1; //no used + uint32_t pp_en:1; //bit18 + uint32_t reserved2:1; + uint32_t use_efifo_en:1; //bit20 + uint32_t psr_post_sel:1; //bit21 + uint32_t reserved3:1; + uint32_t frc_data_format:1; //bit23, 0: YUV444 1: YUV422 + uint32_t capt_bitwidth:1; //bit24, 0: 8bit; 1: 10bit + uint32_t psr_bitwidth:1; //bit25, 0: 8bit; 1: 10bit + uint32_t dbc_lce_en:1; //bit26 + uint32_t dpp_en:1; //bit27 + uint32_t reserved4:4; +}; + +enum iris_frc_prepare_state { + IRIS_FRC_PRE_TX_SWITCH = 0x00, + IRIS_FRC_PATH_PROXY = 0x1, + IRIS_FRC_WAIT_PREPARE_DONE = 0x02, + IRIS_FRC_PRE_DONE = 0x03, + IRIS_FRC_PRE_TIMEOUT = 0x04, +}; + +enum iris_frc_cancel_state { + IRIS_FRC_CANCEL_PATH_PROXY = 0x0, + IRIS_FRC_CANCEL_TX_SWITCH = 0x01, + IRIS_FRC_CANCEL_DONE = 0x02, +}; + +enum iris_mode_rfb2frc_state { + IRIS_RFB_FRC_SWITCH_COMMAND = 0x00, + IRIS_RFB_FRC_SWITCH_DONE = 0x01, +}; + +enum iris_rfb_prepare_state { + IRIS_RFB_PATH_PROXY = 0x0, + IRIS_RFB_WAIT_PREPARE_DONE = 0x01, + IRIS_RFB_PRE_DONE = 0x02, +}; + +enum iris_mode_frc2rfb_state { + IRIS_FRC_RFB_SWITCH_COMMAND = 0x00, + IRIS_FRC_RFB_DATA_PATH = 0x01, + IRIS_FRC_RFB_SWITCH_DONE = 0x02, + IRIS_FRC_RFB_TX_SWITCH = 0x03, +}; + +enum iris_pt_prepare_state { + IRIS_PT_PATH_PROXY = 0x0, + IRIS_PT_WAIT_PREPARE_DONE = 0x01, + IRIS_PT_PRE_DONE = 0x02, +}; + +enum iris_mode_pt2rfb_state { + IRIS_PT_RFB_SWITCH_COMMAND = 0x00, + IRIS_PT_RFB_DATA_PATH = 0x01, + IRIS_PT_RFB_SWITCH_DONE = 0x02, +}; + +enum iris_mode_rfb2pt_state { + IRIS_RFB_PT_SWITCH_COMMAND = 0x00, + IRIS_RFB_PT_DATA_PATH = 0x01, + IRIS_RFB_PT_SWITCH_DONE = 0x02, +}; + +int iris_wait_for_vsync(struct mdss_mdp_ctl *ctl); +void mdss_dsi_iris_init(struct msm_fb_data_type *mfd); +int iris_frc_enable(struct mdss_mdp_ctl *ctl, int enable); +void iris_frc_new_frame(struct mdss_mdp_ctl *ctl); +void iris_mcuclk_divider_change(struct mdss_dsi_ctrl_pdata *ctrl, char lowMcu); +void mdss_dsi_panel_cmds_send_ex(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *pcmds); +int iris_register_write(struct msm_fb_data_type *mfd, u32 addr, u32 value); +bool iris_frc_repeat(struct msm_fb_data_type *mfd); +int iris_set_configure(struct msm_fb_data_type *mfd); +int iris_proc_frcc_setting(struct msm_fb_data_type *mfd); +int iris_calc_meta(struct msm_fb_data_type *mfd); +void iris_copy_meta(struct msm_fb_data_type *mfd); +int iris_dynamic_fps_set(struct mdss_panel_data *pdata, int curr_fps, int new_fps); +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) +int iris_Drc_LPMemc_update(struct msm_fb_data_type *mfd); +int iris_get_frc_timing(struct msm_fb_data_type *mfd, void __user *argp); +void iris_calc_drc_exit(struct msm_fb_data_type *mfd); +#endif +void iris_dtg_para_set(int input_mode, int output_mode); +int iris_get_available_mode(struct msm_fb_data_type *mfd, void __user *argp); +int iris_frc_path_update(struct msm_fb_data_type *mfd, void __user *argp); +void iris_send_meta_video(struct mdss_mdp_ctl *ctl); +void iris_send_meta_cmd(struct mdss_mdp_ctl *ctl); +void iris_cmd_cadence_check(struct mdss_mdp_ctl *ctl); +void iris_calc_nrv(struct mdss_mdp_ctl *ctl); +void iris_i2c_send_meta(struct mdss_mdp_ctl *ctl); +void iris_reg_add(u32 addr, u32 val); +void iris_regs_clear(void); +void iris_update_configure(void); +u32 iris_get_vtotal(struct iris_timing_info *info); +//int iris_proc_constant_ratio(struct iris_config *iris_cfg); +#if !defined(FPGA_PLATFORM) +u32 iris_pi_read(struct mdss_dsi_ctrl_pdata *ctrl, u32 addr); +//u32 iris_pi_write(struct mdss_dsi_ctrl_pdata *ctrl, u32 addr, u32 value); +#endif +u32 iris_lp_memc_calc(u32 value); +bool iris_is_dbc_setting_disable(void); +bool iris_is_lce_setting_disable(void); +bool iris_is_cmdin_videout(void); +//void iris_set_te(struct iris_config *iris_cfg, int te_flag); +bool iris_is_peaking_setting_disable(void); +bool iris_is_cm_setting_disable(void); +#endif //MDSS_DSI_IRIS_H diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.c new file mode 100644 index 000000000000..f01b84133a14 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_fb.h" +#include "mdss_dsi.h" +#include "mdss_dsi_iris2p.h" +#include "mdss_i2c_iris.h" +#include "mdss_dsi_iris2p_def.h" +#include "mdss_dsi_iris2p_extern.h" +#include "mdss_dsi_iris2p_dbg.h" +#include "mdss_debug.h" + +static void iris_nfrv_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t vtime) +{ + //u32 off, mixercfg; + + printk(KERN_DEBUG "#### %s:%d vtime=%lld\n", __func__, __LINE__, vtime.tv64); + /* + mixercfg = MDSS_MDP_LM_BORDER_COLOR; + off = MDSS_MDP_REG_CTL_LAYER(0); + mdss_mdp_ctl_write(ctl, off, mixercfg); + */ + ctl->force_screen_state = MDSS_SCREEN_FORCE_BLANK; +} + +static struct mdss_mdp_vsync_handler nfrv_vsync_handler = { + .vsync_handler = iris_nfrv_vsync_handler, +}; + +static int iris_fbo_enable(struct msm_fb_data_type *mfd, int enable) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_ctl *ctl = mdp5_data->ctl; + + if (enable && !mfd->iris_fbo_enable) { + mfd->iris_fbo_enable = true; + ctl->ops.add_vsync_handler(ctl, &nfrv_vsync_handler); + pr_err("%s:%d enable\n", __func__, __LINE__); + } else if (!enable && mfd->iris_fbo_enable) { + mfd->iris_fbo_enable = false; + ctl->ops.remove_vsync_handler(ctl, &nfrv_vsync_handler); + ctl->force_screen_state = MDSS_SCREEN_DEFAULT; + } + + return 0; +} + +static int iris_sbs_enable(struct msm_fb_data_type *mfd, int enable) +{ + if (enable && !mfd->iris_sbs_enable) { + mfd->iris_sbs_enable = true; + pr_err("%s:%d enable\n", __func__, __LINE__); + } else if (!enable && mfd->iris_sbs_enable) { + mfd->iris_sbs_enable = false; + pr_err("%s:%d disable\n", __func__, __LINE__); + } + + return 0; +} + +static ssize_t iris_dbg_fbo_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + struct msm_fb_data_type *mfd = g_mfd; + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + + switch (val) { + case 0: + pr_info("%s:%d native frame rate video disable\n", __func__, __LINE__); + iris_fbo_enable(mfd, 0); + break; + case 1: + pr_info("%s:%d native frame rate video enable\n", __func__, __LINE__); + iris_fbo_enable(mfd, 1); + break; + default: + pr_err("%s:%d invalid input\n", __func__, __LINE__); + break; + } + + return count; +} + +static const struct file_operations iris_dbg_fbo_fops = { + .open = simple_open, + .write = iris_dbg_fbo_write, +}; + +static ssize_t iris_dbg_sbs_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + struct msm_fb_data_type *mfd = g_mfd; + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + + pr_info("%s:%d sbs_enable %li\n", __func__, __LINE__, val); + switch (val) { + case 0: + iris_sbs_enable(mfd, 0); + break; + case 1: + iris_sbs_enable(mfd, 1); + break; + default: + pr_err("%s:%d invalid input\n", __func__, __LINE__); + break; + } + + return count; +} + +static const struct file_operations iris_dbg_sbs_fops = { + .open = simple_open, + .write = iris_dbg_sbs_write, +}; + + +static bool debug_vsync_enabled; +static void debug_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t vtime) +{ + // NOP +} + +static struct mdss_mdp_vsync_handler iris_debug_vsync_handler = { + .vsync_handler = debug_vsync_handler, +}; + +static ssize_t iris_dbg_vsync_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + struct msm_fb_data_type *mfd = g_mfd; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_ctl *ctl = mdp5_data->ctl; + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + pr_info("%s:%d vsync_enable %li\n", __func__, __LINE__, val); + if (val && !debug_vsync_enabled) { + ctl->ops.add_vsync_handler(ctl, &iris_debug_vsync_handler); + debug_vsync_enabled = true; + } else if (!val && debug_vsync_enabled) { + ctl->ops.remove_vsync_handler(ctl, &iris_debug_vsync_handler); + debug_vsync_enabled = false; + } + return count; +} + +static const struct file_operations iris_dbg_vsync_fops = { + .open = simple_open, + .write = iris_dbg_vsync_write, +}; + +static ssize_t iris_dbg_meta_enable_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + struct msm_fb_data_type *mfd = g_mfd; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + unsigned long val; + uint32_t r; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + pr_info("%s:%d meta_enabled %u in/out %u/%u\n", __func__, __LINE__, (u32)val, iris_cfg->input_frame_rate, iris_cfg->output_frame_rate); + debug_send_meta_enabled = val; + + r = gcd(mfd->iris_conf.input_frame_rate, mfd->iris_conf.output_frame_rate); + mfd->iris_conf.in_ratio = mfd->iris_conf.input_frame_rate / r; + mfd->iris_conf.out_ratio = mfd->iris_conf.output_frame_rate / r; + + iris_register_write(mfd, IRIS_PWIL_ADDR + 0x0638, + (iris_cfg->out_ratio << IRIS_PWIL_OUT_FRAME_SHIFT)); + iris_register_write(mfd, IRIS_PWIL_ADDR + 0x12FC, + (iris_cfg->in_ratio << IRIS_PWIL_IN_FRAME_SHIFT)); + return count; +} + +static ssize_t iris_dbg_meta_enable_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + int len, tot = 0; + char bp[512]; + if (*ppos) + return 0; + + len = sizeof(bp); + tot = scnprintf(bp, len, "%u\n", debug_send_meta_enabled); + + if (copy_to_user(buff, bp, tot)) + return -EFAULT; + + *ppos += tot; + + return tot; +} + +static const struct file_operations iris_dbg_meta_fops = { + .open = simple_open, + .write = iris_dbg_meta_enable_write, + .read = iris_dbg_meta_enable_read, +}; + +int iris_debugfs_init(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + + iris_cfg->dbg_root = debugfs_create_dir("iris", NULL); + if (IS_ERR_OR_NULL(iris_cfg->dbg_root)) { + pr_err("debugfs_create_dir for iris_debug failed, error %ld\n", + PTR_ERR(iris_cfg->dbg_root)); + return -ENODEV; + } + + debugfs_create_u32("set_ratio", 0644, iris_cfg->dbg_root, + (u32 *)&debug_ratio_enabled); + + debugfs_create_u32("set_mode_switch", 0644, iris_cfg->dbg_root, + (u32 *)&debug_mode_switch_enabled); + + debugfs_create_u32("set_repeat", 0644, iris_cfg->dbg_root, + (u32 *)&debug_repeat_enabled); + + debugfs_create_u32("set_dtg", 0644, iris_cfg->dbg_root, + (u32 *)&debug_dtg_enabled); + + debugfs_create_u32("set_te", 0644, iris_cfg->dbg_root, + (u32 *)&debug_te_enabled); + + debugfs_create_u32("set_new_frame", 0644, iris_cfg->dbg_root, + (u32 *)&debug_new_frame_enabled); + + debugfs_create_u32("new_repeat", 0644, iris_cfg->dbg_root, + (u32 *)&debug_new_repeat); + + debugfs_create_u32("true_cut", 0644, iris_cfg->dbg_root, + (u32 *)&debug_true_cut); + + debugfs_create_u32("frc_path", 0644, iris_cfg->dbg_root, + (u32 *)&g_mfd->iris_conf.frc_path); + + debugfs_create_u32("input_vfr", 0644, iris_cfg->dbg_root, + (u32 *)&g_mfd->iris_conf.input_vfr); + + debugfs_create_u32("set_hlmd", 0644, iris_cfg->dbg_root, + (u32 *)&debug_hlmd_enabled); + + debugfs_create_u32("debug_pt", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_pt); + + debugfs_create_u32("debug_bypass", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_bypass); + + debugfs_create_u32("debug_kickoff60", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_kickoff60); + + debugfs_create_u32("debug_lastframerepeat", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_lastframerepeat); + + debugfs_create_u32("debug_pt_disable", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_pt_disable); + + debugfs_create_u32("debug_dtg_v12", 0644, iris_cfg->dbg_root, + (u32 *)&iris_debug_dtg_v12); + + debugfs_create_u32("dsi_mode_in_rfb", 0644, iris_cfg->dbg_root, + (u32 *)&iris_cfg->avail_mode.dsi_mode_in_rfb); + + debugfs_create_u32("dsi_mode_in_ptl", 0644, iris_cfg->dbg_root, + (u32 *)&iris_cfg->avail_mode.dsi_mode_in_ptl); + + debugfs_create_u32("dsi_mode_in_pth", 0644, iris_cfg->dbg_root, + (u32 *)&iris_cfg->avail_mode.dsi_mode_in_pth); + + if (debugfs_create_file("fbo", 0644, iris_cfg->dbg_root, mfd, + &iris_dbg_fbo_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (debugfs_create_file("sbs", 0644, iris_cfg->dbg_root, mfd, + &iris_dbg_sbs_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (debugfs_create_file("vsync_debug", 0644, iris_cfg->dbg_root, mfd, + &iris_dbg_vsync_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + if (debugfs_create_file("send_meta", 0644, iris_cfg->dbg_root, mfd, + &iris_dbg_meta_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.h new file mode 100644 index 000000000000..c77ca340917f --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_dbg.h @@ -0,0 +1,5 @@ +#ifndef MDSS_DSI_IRIS_DBG_H +#define MDSS_DSI_IRIS_DBG_H +int iris_debugfs_init(struct msm_fb_data_type *mfd); + +#endif diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.c new file mode 100644 index 000000000000..cbadf66b0d83 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.c @@ -0,0 +1,108 @@ +#include "mdss_dsi_iris2p.h" +#include "mdss_dsi_iris2p_def.h" +// iris2, iris workmode + +struct iris_info_t iris_info; + +struct msm_fb_data_type *g_mfd; +struct mdss_dsi_ctrl_pdata *g_dsi_ctrl; + +/* debug send meta */ +bool debug_send_meta_enabled = 1; +/* debug new frame flag in video mode */ +int debug_new_frame_enabled = 1; +/* debug in/out ratio flag */ +int debug_ratio_enabled = 0; +int debug_hlmd_enabled = 0; +/* debug repeat flag */ +int debug_repeat_enabled = 1; +/* debug te flag */ +int debug_te_enabled = 1; +/* debug dtg */ +int debug_dtg_enabled; +bool frc_repeat_enter; +int debug_new_repeat = 1; +/* debug send mode switch */ +int debug_mode_switch_enabled = 1; +int debug_true_cut = 1; +int iris_debug_pt = 0; +int iris_debug_bypass = 0; +int iris_debug_kickoff60 = 0; +int iris_debug_lastframerepeat = 0; +int iris_debug_pt_disable = 0; +int iris_debug_dtg_v12 = 1; + +// FIXME mdp5 use add vsync handler and no find DMA_P bit +//void mdp4_dsi_video_wait4dmap_done(int cndx); + +/* Activate Delay 0, FBO Enable: 0, Display Mode: Normal, + * PSR Command: PSR Enable, Capture Enable: - + */ +char pt_enable[2] = { 0x00, 0x00 }; +char memc_enable[2] = {0x04, 0x2}; +char bypass_enable = 0xff; +u16 lp_memc_timing[] = {IRIS_DTG_HRES_SETTING, IRIS_DTG_VRES_SETTING}; + +char iris_pt_enter_cmds[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000005), //valid word number + 0x00, //burst mode + 0x00, //reserved + PWIL_U16(0x0004), //burst length + PWIL_U32(IRIS_DATA_PATH_ADDR), //proxy MB2 + PWIL_U32(0x800000), + PWIL_U32(IRIS_MODE_ADDR), //proxy MB1 + PWIL_U32(0x800000) +}; + +char iris_rfb_enter_cmds[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000005), //valid word number + 0x00, //burst mode + 0x00, //reserved + PWIL_U16(0x0004), //burst length + PWIL_U32(IRIS_DATA_PATH_ADDR), //proxy MB2 + PWIL_U32(0x800000), + PWIL_U32(IRIS_MODE_ADDR), //proxy MB1 + PWIL_U32(0x800001) +}; + +char iris_memc_enter_cmds[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000005), //valid word number + 0x00, //burst mode + 0x00, //reserved + PWIL_U16(0x0004), //burst length + PWIL_U32(IRIS_DATA_PATH_ADDR), //proxy MB2 + PWIL_U32(0x800000), + PWIL_U32(IRIS_MODE_ADDR), //proxy MB1 + PWIL_U32(0x800002) +}; + +struct dsi_cmd_desc pt_data_path_config[1] = { + { { DTYPE_GEN_LWRITE, 1, 0, 0, 0, + sizeof(iris_pt_enter_cmds) }, iris_pt_enter_cmds}, +}; + +struct dsi_cmd_desc rfb_data_path_config[] = { + { { DTYPE_GEN_LWRITE, 1, 0, 0, 0, + sizeof(iris_rfb_enter_cmds) }, iris_rfb_enter_cmds}, +}; + +struct dsi_cmd_desc pt_mode_enter[1] = { + { { DTYPE_GEN_WRITE2, 1, 0, 0, 0, + sizeof(pt_enable) }, pt_enable}, +}; + +struct dsi_cmd_desc memc_data_path_config[1] = { + { { DTYPE_GEN_LWRITE, 1, 0, 0, 0, + sizeof(iris_memc_enter_cmds) }, iris_memc_enter_cmds}, +}; + +struct dsi_cmd_desc memc_mode_enter[1] = { + { { DTYPE_GEN_WRITE2, 1, 0, 0, 0, + sizeof(memc_enable) }, memc_enable}, +}; diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.h new file mode 100644 index 000000000000..d4aca11d9672 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_def.h @@ -0,0 +1,154 @@ +#ifndef MDSS_DSI_IRIS_DEF_H +#define MDSS_DSI_IRIS_DEF_H + +#define IRIS_CHIP_HW_VER 1 + +#define RATIO_NUM (8) + +// assume 1 entry 1 ms +#define IRIS_CMD_FIFO_EMPTY 16 +#define IRIS_REPEAT_NO 0 +#define IRIS_REPEAT_FORCE 1 +#define IRIS_REPEAT_CAPDIS 2 + +/* Per Panel different */ +#define IRIS_DTG_EVS_DLY 224 + +#define META_PKT_SIZE 512 +#define META_HEADER 16 + +#define IMG720P_VSIZE (720) +#ifdef ENABLE_IRIS2_480X800_PANEL +#define IRIS_DTG_HRES_SETTING 480 +#define IRIS_DTG_VRES_SETTING 800 +#else +#define IRIS_DTG_HRES_SETTING 768 +#define IRIS_DTG_VRES_SETTING 2048 +#endif + +#define LEVEL_MAX (5) +#define IMG1080P_HSIZE (1920) +#define IMG1080P_VSIZE (1080) +#define IMG720P_HSIZE (1280) + +#if 0 +/* Assume the panel can accept this period increase */ +#define IRIS_DELTA_VT_P (31) +/* VFP needs at least 2 lines VSW needs at least 1 line */ +#define IRIS_DELTA_VT_M (-4) +#endif + +// MB3 +struct iris_pq_setting { + uint32_t peaking:4; + uint32_t sharpness:4; + uint32_t memcdemo:4; + uint32_t gamma:2; + uint32_t memclevel:2; + uint32_t contrast:8; + uint32_t cinema_en:1; + uint32_t peakingdemo:4; + uint32_t reserved:2; + uint32_t update:1; +}; + +// MB5 +struct iris_dbc_setting { + uint32_t update:1; + uint32_t reserved:7; + uint32_t brightness:7; + uint32_t ext_pwm:1; + uint32_t cabcmode:4; + uint32_t dlv_sensitivity:12; +}; + +struct iris_lce_setting { + uint32_t mode:4; + uint32_t mode1level:4; + uint32_t mode2level:4; + uint32_t demomode:4; + uint32_t reserved:15; + uint32_t update:1; +}; + +struct iris_cm_setting { + uint32_t cm6axes:3; + uint32_t cm3d:5; + uint32_t demomode:3; + uint32_t ftc_en:1; + uint32_t reserved:19; + uint32_t update:1; +}; + +struct iris_config_setting { + int update; + u8 level; + uint32_t value; +}; + +struct iris_conf_update { + uint32_t demo_win_fi:1; + uint32_t pq_setting:1; + uint32_t dbc_setting:1; + uint32_t lp_memc_setting:1; + uint32_t color_adjust:1; + uint32_t lce_setting:1; + uint32_t cm_setting:1; + uint32_t reserved:25; +}; +// --------------------------------------------------------------------------- +//! Structure definition for demo window. +// --------------------------------------------------------------------------- +struct demo_win_info { + int startx; //12bits width + int starty; //12bits width + int endx; //12bits width + int endy; //12bits width + int color; //Y U V 8bits width, Y[7:0], U[15:8], V[23:16] + int borderwidth; ///3bits width + int fi_demo_en; //bool + int sharpness_en; //bool + int cm_demo_en; //bool +}; + +// --------------------------------------------------------------------------- +//! Structure definition for demo window FI setting. +// --------------------------------------------------------------------------- +struct fi_demo_win { + int startx; //12bits width panel position + int starty; //12bits width panel position + int endx; //12bits width panel position + int endy; //12bits width panel position + int borderwidth; ///3bits width + int colsize; + int color; + int rowsize; + int modectrl; +}; + +struct peaking_demo_win { + int startx; //12bits width panel position + int starty; //12bits width panel position + int endx; //12bits width panel position + int endy; //12bits width panel position + int sharpness_en; //bool +}; + +struct cm_demo_win { + int startx; //12bits width panel position + int starty; //12bits width panel position + int endx; //12bits width panel position + int endy; //12bits width panel position + int cm_demo_en; //bool +}; + +struct quality_setting { + struct iris_pq_setting pq_setting; + struct iris_dbc_setting dbc_setting; + struct iris_config_setting lp_memc_setting; + struct iris_lce_setting lce_setting; + struct iris_cm_setting cm_setting; + u8 color_adjust; +}; + +#endif diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_extern.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_extern.h new file mode 100644 index 000000000000..f0ee13e33c35 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_extern.h @@ -0,0 +1,56 @@ +#ifndef MDSS_DSI_IRIS_EXTERN_H +#define MDSS_DSI_IRIS_EXTERN_H +#include "mdss_dsi_iris2p_def.h" +// iris2, iris workmode +extern struct iris_info_t iris_info; +extern struct msm_fb_data_type *g_mfd; +extern struct mdss_dsi_ctrl_pdata *g_dsi_ctrl; + +/* debug send meta */ +extern bool debug_send_meta_enabled; +/* debug new frame flag in video mode */ +extern int debug_new_frame_enabled; +/* debug in/out ratio flag */ +extern int debug_ratio_enabled; +extern int debug_hlmd_enabled; +/* debug repeat flag */ +extern int debug_repeat_enabled; +/* debug te flag */ +extern int debug_te_enabled; +/* debug dtg */ +extern int debug_dtg_enabled; +extern bool frc_repeat_enter; +extern int debug_new_repeat; +/* debug send mode switch */ +extern int debug_mode_switch_enabled; +extern int debug_true_cut; +extern int iris_debug_pt; +extern int iris_debug_bypass; +extern int iris_debug_kickoff60; +extern int iris_debug_lastframerepeat; +extern int iris_debug_pt_disable; +extern int iris_debug_dtg_v12; + +// FIXME mdp5 use add vsync handler and no find DMA_P bit +//void mdp4_dsi_video_wait4dmap_done(int cndx); + +/* Activate Delay 0, FBO Enable: 0, Display Mode: Normal, + * PSR Command: PSR Enable, Capture Enable: - + */ +extern char pt_enable[2]; +extern char memc_enable[2]; +extern char bypass_enable; +extern u16 lp_memc_timing[]; + +extern char iris_pt_enter_cmds[]; +extern char iris_rfb_enter_cmds[]; +extern char iris_memc_enter_cmds[]; + +extern struct dsi_cmd_desc pt_data_path_config[1]; +extern struct dsi_cmd_desc rfb_data_path_config[1]; +extern struct dsi_cmd_desc pt_mode_enter[1]; + +extern struct dsi_cmd_desc memc_data_path_config[1]; + +extern struct dsi_cmd_desc memc_mode_enter[1]; +#endif diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.c new file mode 100644 index 000000000000..2d9a842c66d5 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.c @@ -0,0 +1,1282 @@ +#include +#include +#include +#include +#include +#include +#include +#include "mdss_debug.h" +#include "mdss_dsi_iris2p.h" +#include "mdss_panel.h" +#include "mdss_i2c_iris.h" +#include "mdss_dsi_iris2p_def.h" +#include "mdss_dsi_iris2p_extern.h" +#include "mdss_dsi_iris2p_ioctl.h" +#include "mdss_dsi_iris2p_mode_switch.h" + +static struct completion iris_vsync_comp; +static struct demo_win_info iris_demo_win_info; +static uint16_t ratio[LEVEL_MAX] = { 8, 6, 5, 4, 4 }; +static uint16_t ratio_720[LEVEL_MAX] = { 8, 6, 5, 4, 3}; +static uint16_t ratio_16to10_level[LEVEL_MAX][2] = { + {1212, 758}, + {1152, 720}, + {1024, 640}, + {640, 400}, + {480, 300} +}; + +static uint16_t ratio_16to9_level[LEVEL_MAX][2] = { + {1280, 720}, + {1280, 720}, + {1024, 576}, + {640, 360}, + {480, 270} +}; + +static uint16_t ratio_4to3_level[LEVEL_MAX][2] = { + {1098, 824}, + {1024, 768}, + {800, 600}, + {640, 480}, + {480, 360} +}; + +static void mdss_iris_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t vtime) +{ + printk("#### %s:%d vtime=%lld\n", __func__, __LINE__, vtime.tv64); + complete(&iris_vsync_comp); +} + +static struct mdss_mdp_vsync_handler iris_vsync_handler = { + .vsync_handler = mdss_iris_vsync_handler, +}; + +int iris_wait_for_vsync(struct mdss_mdp_ctl *ctl) +{ + int rc; + + printk("#### %s:%d\n", __func__, __LINE__); + init_completion(&iris_vsync_comp); + ctl->ops.add_vsync_handler(ctl, &iris_vsync_handler); + rc = wait_for_completion_interruptible_timeout( + &iris_vsync_comp, msecs_to_jiffies(100)); + ctl->ops.remove_vsync_handler(ctl, &iris_vsync_handler); + if (rc < 0) + printk("#### %s:%d: error %d\n", __func__, __LINE__, rc); + else if (rc == 0) { + printk("#### %s:%d: timeout\n", __func__, __LINE__); + rc = -ETIMEDOUT; + } + return rc; +} + + +int iris_dynamic_fps_set(struct mdss_panel_data *pdata, int curr_fps, int new_fps) +{ + int ret = -1; + int add_v_lines = 0; + int vsync_period, hsync_period; + int diff; + struct mdss_dsi_ctrl_pdata *ctrl = g_dsi_ctrl; + struct dsi_panel_cmds panel_cmds; + + static char iris_dtg_dfps_cmds[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000005), //valid word number + 0x00, //burst mode + 0x00, //reserved + PWIL_U16(0x0004), //burst length + PWIL_U32(IRIS_DTG_ADDR + 0x14), + PWIL_U32(0x000000), + PWIL_U32(IRIS_DTG_ADDR + 0x10000), + PWIL_U32(0x10000), + }; + + static struct dsi_cmd_desc dtg_dfps_config[] = { + { { DTYPE_GEN_LWRITE, 1, 0, 0, 0, + sizeof(iris_dtg_dfps_cmds) }, iris_dtg_dfps_cmds}, + }; + + vsync_period = iris_info.output_timing.vbp + iris_info.output_timing.vfp + iris_info.output_timing.vsw + iris_info.output_timing.vres; + hsync_period = iris_info.output_timing.hbp + iris_info.output_timing.hfp + iris_info.output_timing.hsw + iris_info.output_timing.hres; + + pr_info("#######%s, %d. vbp = %d, vfp = %d, vsw = %d, vsync_period = %d.\n", __func__, __LINE__, + iris_info.output_timing.vbp, iris_info.output_timing.vfp, iris_info.output_timing.vsw, vsync_period); + + diff = curr_fps - new_fps; + + add_v_lines = mult_frac(vsync_period, diff, new_fps); + iris_info.output_timing.vfp += add_v_lines; + + pr_info("#######%s, %d. diff = %d, add_v_lines = %d, v_front_porch = %d, vsync_period = %d.\n", __func__, __LINE__, + diff, add_v_lines, iris_info.output_timing.vfp, vsync_period); + + iris_dtg_dfps_cmds[20] = (__u8)(iris_info.output_timing.vfp & 0xff); + iris_dtg_dfps_cmds[21] = (__u8)((iris_info.output_timing.vfp >> 8) & 0xff); + + panel_cmds.cmds = dtg_dfps_config; + panel_cmds.cmd_cnt = ARRAY_SIZE(dtg_dfps_config); + panel_cmds.link_state = DSI_HS_MODE; + mdss_dsi_panel_cmds_send_ex(ctrl, &panel_cmds); + + return ret; +} + + +static int irisDsiStsGet(struct mdss_dsi_ctrl_pdata *ctrl) +{ + //8094_TODO; + return IRIS_CONFIGURE_GET_VALUE_CORRECT; +} + +u32 iris_lp_memc_calc(u32 value) +{ +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + struct iris_config *iris_cfg = &g_mfd->iris_conf; +#endif + uint32_t hres, vres; + uint32_t frc_timing = (uint32_t)lp_memc_timing[1] << 16 | (uint32_t)lp_memc_timing[0]; + static enum res_ratio ratio_type; + + hres = iris_info.work_mode.rx_ch ? (uint32_t)iris_info.input_timing.hres * 2 : (uint32_t)iris_info.input_timing.hres; + vres = (uint32_t)iris_info.input_timing.vres; + + if (value >= LEVEL_MAX) + { + value = LEVEL_MAX - 1; + pr_err("#### %s:%d, Low Power MEMC level is out of range.\n", __func__, __LINE__); + } + + pr_debug("#### %s:%d, Low Power MEMC level hres = %d, vres = %d, rx_ch = %d.\n", __func__, __LINE__, + hres, vres, iris_info.work_mode.rx_ch); + + if(((hres / 4) == (vres / 3)) || ((hres / 3) == (vres / 4))) + { + ratio_type = ratio_4to3; + } + else if(((hres / 16) == (vres / 10)) || ((hres / 10) == (vres / 16))) + { + ratio_type = ratio_16to10; + } + else + { + ratio_type = ratio_16to9; + } + + if ((hres * vres) >= ((uint32_t) IMG1080P_HSIZE * (uint32_t) IMG1080P_VSIZE)) + { + switch (ratio_type) { + case ratio_4to3: + lp_memc_timing[0] = ratio_4to3_level[value][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_4to3_level[value][hres > vres ? 1 : 0]; + if (hres*10 / (uint32_t)lp_memc_timing[0] > 40) + { + lp_memc_timing[0] = ratio_4to3_level[value-1][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_4to3_level[value-1][hres > vres ? 1 : 0]; + } + break; + case ratio_16to10: + lp_memc_timing[0] = ratio_16to10_level[value][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_16to10_level[value][hres > vres ? 1 : 0]; + if (hres*10 / (uint32_t)lp_memc_timing[0] > 40) { + lp_memc_timing[0] = ratio_16to10_level[value-1][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_16to10_level[value-1][hres > vres ? 1 : 0]; + } + break; + case ratio_16to9: + lp_memc_timing[0] = ratio_16to9_level[value][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_16to9_level[value][hres > vres ? 1 : 0]; + if (hres*10 / (uint32_t)lp_memc_timing[0] > 40) { + lp_memc_timing[0] = ratio_16to9_level[value-1][hres > vres ? 0 : 1]; + lp_memc_timing[1] = ratio_16to9_level[value-1][hres > vres ? 1 : 0]; + } + break; + default: + break; + }; + } else if ((hres * vres) > ((uint32_t) IMG720P_HSIZE * (uint32_t) IMG720P_VSIZE)) { + lp_memc_timing[0] = hres * ratio_720[value] / RATIO_NUM; + lp_memc_timing[1] = vres * ratio_720[value] / RATIO_NUM; + } else { + lp_memc_timing[0] = hres * ratio[value] / RATIO_NUM; + lp_memc_timing[1] = vres * ratio[value] / RATIO_NUM; + } + + frc_timing = (uint32_t)lp_memc_timing[1] << 16 | (uint32_t)lp_memc_timing[0]; + +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + iris_cfg->lp_frc_timing = frc_timing; +#endif + pr_debug("#### %s:%d,wHres: %d, wVres: %d, low power memc timing: 0x%x\n", __func__, __LINE__, lp_memc_timing[0], lp_memc_timing[1], frc_timing); + + return frc_timing; +} + +static int iris_configure(struct msm_fb_data_type *mfd, u32 type, u32 value) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + struct quality_setting * pqlt_cur_setting = & iris_info.setting_info.quality_cur; + u32 configAddr = 0; + u32 configValue = 0; + + pr_debug("iris_configure: %d - 0x%x\n", type, value); + + if (type >= IRIS_CONFIG_TYPE_MAX) + return -EINVAL; + + mutex_lock(&iris_cfg->config_mutex); + switch (type) { + case IRIS_PEAKING: + pqlt_cur_setting->pq_setting.peaking = value & 0xf; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_SHARPNESS: + pqlt_cur_setting->pq_setting.sharpness = value & 0xf; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_MEMC_DEMO: + pqlt_cur_setting->pq_setting.memcdemo = value & 0xf; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_PEAKING_DEMO: + pqlt_cur_setting->pq_setting.peakingdemo = value & 0xf; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_GAMMA: + pqlt_cur_setting->pq_setting.gamma = value & 0x3; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_MEMC_LEVEL: + pqlt_cur_setting->pq_setting.memclevel = value & 0x3; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_CONTRAST: + pqlt_cur_setting->pq_setting.contrast = value & 0xff; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_BRIGHTNESS: + pqlt_cur_setting->dbc_setting.brightness = value & 0x7f; + pqlt_cur_setting->dbc_setting.update = 1; + iris_info.update.dbc_setting = true; + break; + case IRIS_EXTERNAL_PWM: + pqlt_cur_setting->dbc_setting.ext_pwm = value & 0x1; + pqlt_cur_setting->dbc_setting.update = 1; + iris_info.update.dbc_setting = true; + break; + case IRIS_DBC_QUALITY: + pqlt_cur_setting->dbc_setting.cabcmode = value & 0xf; + pqlt_cur_setting->dbc_setting.update = 1; + iris_info.update.dbc_setting = true; + break; + case IRIS_DLV_SENSITIVITY: + pqlt_cur_setting->dbc_setting.dlv_sensitivity = value & 0xfff; + pqlt_cur_setting->dbc_setting.update = 1; + iris_info.update.dbc_setting = true; + break; + case IRIS_DBC_CONFIG: + pqlt_cur_setting->dbc_setting = *((struct iris_dbc_setting *)&value); + pqlt_cur_setting->dbc_setting.update = 1; + iris_info.update.dbc_setting = true; + break; + case IRIS_PQ_CONFIG: + value |= pqlt_cur_setting->pq_setting.cinema_en << 24; + pqlt_cur_setting->pq_setting = *((struct iris_pq_setting *)&value); + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + case IRIS_LPMEMC_CONFIG: + pqlt_cur_setting->lp_memc_setting.level = value; + pqlt_cur_setting->lp_memc_setting.value = iris_lp_memc_calc(value); + iris_info.update.lp_memc_setting = true; + break; + case IRIS_COLOR_ADJUST: + pqlt_cur_setting->color_adjust = value & 0xff; + iris_info.update.color_adjust = true; + break; + case IRIS_LCE_SETTING: + pqlt_cur_setting->lce_setting.mode = value & 0xf; + pqlt_cur_setting->lce_setting.mode1level = (value & 0xf0) >> 4; + pqlt_cur_setting->lce_setting.mode2level = (value & 0xf00) >> 8; + pqlt_cur_setting->lce_setting.demomode = (value & 0xf000) >> 12; + pqlt_cur_setting->lce_setting.update = 1; + iris_info.update.lce_setting = true; + break; + case IRIS_CM_SETTING: + pqlt_cur_setting->cm_setting.cm6axes = value & 0x07; + pqlt_cur_setting->cm_setting.cm3d = (value & 0x1f) >> 3; + pqlt_cur_setting->cm_setting.demomode = (value & 0x700) >> 8; + pqlt_cur_setting->cm_setting.ftc_en = (value & 0x800) >> 11; + pqlt_cur_setting->cm_setting.update = 1; + iris_info.update.cm_setting = true; + break; + case IRIS_CINEMA_MODE: + pqlt_cur_setting->pq_setting.cinema_en = value & 0x1; + pqlt_cur_setting->pq_setting.update = 1; + iris_info.update.pq_setting = true; + break; + default: + mutex_unlock(&iris_cfg->config_mutex); + return -EINVAL; + } + + //update dbc mode + if (iris_info.update.dbc_setting) { + if (pqlt_cur_setting->dbc_setting.cabcmode == 0) + iris_info.setting_info.dbc_mode &= ~(1 << 1); + else + iris_info.setting_info.dbc_mode |= 1 << 1; + if (pqlt_cur_setting->dbc_setting.dlv_sensitivity == 0) + iris_info.setting_info.dbc_mode &= ~1; + else + iris_info.setting_info.dbc_mode |= 1; + } + + //TODO this is work around due to fogbug15362 using iris_reg_add to update + if (1) { + mutex_unlock(&iris_cfg->config_mutex); + return 0; + } + + // FIXME other mode, use meta method + if (iris_cfg->sf_notify_mode != IRIS_MODE_RFB) { + mutex_unlock(&iris_cfg->config_mutex); + return 0; + } + + // PQ setting, MB3 + if (iris_info.update.pq_setting) { + configValue = *((u32 *)&pqlt_cur_setting->pq_setting); + configAddr = IRIS_PQ_SETTING_ADDR; + iris_info.update.pq_setting = false; + } else if (iris_info.update.dbc_setting) { + configValue = *((u32 *)&pqlt_cur_setting->dbc_setting); + configAddr = IRIS_DBC_SETTING_ADDR; + iris_info.update.dbc_setting = false; + } else if (iris_info.update.lp_memc_setting) { + configValue = pqlt_cur_setting->lp_memc_setting.value | 0x80000000; + configAddr = IRIS_LPMEMC_SETTING_ADDR; + iris_info.update.lp_memc_setting = false; + } else if (iris_info.update.color_adjust) { + configValue = ( u32 )pqlt_cur_setting->color_adjust | 0x80000000; + configAddr = IRIS_COLOR_ADJUST_ADDR; + iris_info.update.color_adjust = false; + } else if (iris_info.update.lce_setting) { + configValue = *((u32 *)&pqlt_cur_setting->lce_setting); + configAddr = IRIS_LCE_SETTING_ADDR; + iris_info.update.lce_setting = false; + } else if (iris_info.update.cm_setting) { + configValue = *((u32 *)&pqlt_cur_setting->cm_setting); + configAddr = IRIS_CM_SETTING_ADDR; + iris_info.update.cm_setting = false; + } + + if (0 == configValue && 0 == configAddr) { + mutex_unlock(&iris_cfg->config_mutex); + pr_warn(" no configValue and configAddr specified, possibly wrong type(%d)!\n", type); + return -EINVAL; + } + + pr_debug("%s, %d: configAddr = 0x%x, configValue = 0x%x.\n", __func__, __LINE__, configAddr, configValue); + + iris_register_write(mfd, configAddr, configValue); + mutex_unlock(&iris_cfg->config_mutex); + + return 0; +} + + + +int iris_configure_ex(struct msm_fb_data_type *mfd, u32 type, u32 count, u32 *values) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + struct demo_win_info *pdemo_win_info; + + int width, height, frcEndx, frcEndy, frcStartx, frcStarty; + int color = 0, colsize = 0, rowsize = 0, modectrl = 0x3f00, peakingctrl = 0, winstart = 0, winend = 0; + int displaywidth = (iris_info.work_mode.tx_ch ? iris_info.output_timing.hres * 2 : iris_info.output_timing.hres); + int ret; + + pdemo_win_info = (struct demo_win_info *)values; + + memcpy(&iris_demo_win_info, values, sizeof(struct demo_win_info)); + pr_debug("%s: startx =%x, starty=%x, endx=%x, endy=%x, color=%x, boardwidth=%x, fi_demo_en = %x, peakingdemoEn = %x\n", + __func__, iris_demo_win_info.startx, iris_demo_win_info.starty, iris_demo_win_info.endx, iris_demo_win_info.endy, + iris_demo_win_info.color, iris_demo_win_info.borderwidth, + iris_demo_win_info.fi_demo_en, iris_demo_win_info.sharpness_en); + + iris_info.fi_demo_win_info.startx = iris_demo_win_info.startx; + iris_info.fi_demo_win_info.starty = iris_demo_win_info.starty; + iris_info.fi_demo_win_info.endx = iris_demo_win_info.endx; + iris_info.fi_demo_win_info.endy = iris_demo_win_info.endy; + iris_info.fi_demo_win_info.borderwidth = iris_demo_win_info.borderwidth; + + iris_info.peaking_demo_win_info.startx = iris_demo_win_info.startx; + iris_info.peaking_demo_win_info.starty = iris_demo_win_info.starty; + iris_info.peaking_demo_win_info.endx = iris_demo_win_info.endx; + iris_info.peaking_demo_win_info.endy = iris_demo_win_info.endy; + iris_info.peaking_demo_win_info.sharpness_en = iris_demo_win_info.sharpness_en; + + iris_info.cm_demo_win_info.startx = iris_demo_win_info.startx; + iris_info.cm_demo_win_info.starty = iris_demo_win_info.starty; + iris_info.cm_demo_win_info.endx = iris_demo_win_info.endx; + iris_info.cm_demo_win_info.endy = iris_demo_win_info.endy; + iris_info.cm_demo_win_info.cm_demo_en = iris_demo_win_info.cm_demo_en; + + if (displaywidth < 100 || iris_info.output_timing.vres < 100) { + pr_err("panel size too small!\n"); + return -EINVAL; + } + if (pdemo_win_info->startx > displaywidth || + pdemo_win_info->starty > iris_info.output_timing.vres) { + pr_err("user defined window start point over range!\n"); + return -EINVAL; + } + + if (pdemo_win_info->endx > displaywidth || + pdemo_win_info->endy > iris_info.output_timing.vres) { + pr_err("user defined end point over range!\n"); + return -EINVAL; + } + + if (pdemo_win_info->startx > pdemo_win_info->endx || + pdemo_win_info->starty > pdemo_win_info->endy) { + pr_err("user defined start point > end point!\n"); + return -EINVAL; + } + + pr_debug("iris_cfg->nrv_enable: %d\n", iris_cfg->nrv_enable); + if (iris_cfg->nrv_enable) { + width = iris_cfg->meta.nrv.captureRight - iris_cfg->meta.nrv.captureLeft; + height = iris_cfg->meta.nrv.captureBottom - iris_cfg->meta.nrv.captureTop; + frcStartx = pdemo_win_info->startx * width/displaywidth; + frcStarty = pdemo_win_info->starty * height/iris_info.output_timing.vres; + frcEndx = pdemo_win_info->endx * width/displaywidth; + frcEndy = pdemo_win_info->endy * height/iris_info.output_timing.vres; + } else { + frcStartx = pdemo_win_info->startx * lp_memc_timing[0] / displaywidth; + frcStarty = pdemo_win_info->starty * lp_memc_timing[1] / iris_info.output_timing.vres; + frcEndx = pdemo_win_info->endx * lp_memc_timing[0] / displaywidth; + frcEndy = pdemo_win_info->endy * lp_memc_timing[1] / iris_info.output_timing.vres; + } + + pr_debug("frc mode resolution: %d - %d - %d - %d - %d - %d\n", frcStartx, frcStarty, frcEndx, frcEndy, lp_memc_timing[0], lp_memc_timing[1]); + if (frcEndy + pdemo_win_info->borderwidth >= lp_memc_timing[1]) + frcEndy = lp_memc_timing[1] - pdemo_win_info->borderwidth; + winstart = (pdemo_win_info->startx & 0x3fff) + ((pdemo_win_info->starty & 0x3fff) << 16); + winend = (pdemo_win_info->endx & 0x3fff) + ((pdemo_win_info->endy & 0x3fff) << 16); + + peakingctrl = 1 | pdemo_win_info->sharpness_en<<1; + + color = pdemo_win_info->color; + + colsize = (frcStartx & 0xfff) | ((frcEndx & 0xfff)<<16); + rowsize = (frcStarty & 0xfff) | ((frcEndy & 0xfff)<<16); + pr_debug("%s:BorderWidth =%x\n", __func__, pdemo_win_info->borderwidth); + modectrl = modectrl | pdemo_win_info->fi_demo_en; + modectrl = modectrl | 1<<1; + modectrl = modectrl | ((pdemo_win_info->borderwidth & 0x7)<<4); + + pr_debug("%s: COL_SIZE =%x, MODE_RING=%x, ROW_SIZE=%x, STARTWIN=%x, ENDWIN=%x, MODE_CTRL=%x, PEAKING_CTRL = %x\n", + __func__, colsize, color, rowsize, winstart, winend, modectrl, peakingctrl); + + if (pdemo_win_info->fi_demo_en) { + //backup FI setting for demo window, because when setting demo window, the work mode may can't be MEMC mode, so + //FI setting can't write. + iris_info.fi_demo_win_info.colsize = colsize; + iris_info.fi_demo_win_info.color = color; + iris_info.fi_demo_win_info.rowsize = rowsize; + iris_info.fi_demo_win_info.modectrl = modectrl; + iris_info.update.demo_win_fi = true; + + // FIXME set registers + if ((iris_cfg->sf_notify_mode == IRIS_MODE_FRC) || + (iris_cfg->sf_notify_mode == IRIS_MODE_RFB2FRC) || + (iris_cfg->sf_notify_mode == IRIS_MODE_FRC_PREPARE)) { + iris_info.update.demo_win_fi = false; + iris_reg_add(FI_DEMO_COL_SIZE, colsize); + iris_reg_add(FI_DEMO_MODE_RING, color); + iris_reg_add(FI_DEMO_ROW_SIZE, rowsize); + iris_reg_add(FI_DEMO_MODE_CTRL, modectrl); + iris_reg_add(FI_SHADOW_UPDATE, 1); + } + } + if (pdemo_win_info->sharpness_en) { + // FIXME + if (iris_cfg->sf_notify_mode != IRIS_MODE_RFB) { + //mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(PEAKING_STARTWIN, winstart); + iris_reg_add(PEAKING_ENDWIN, winend); + iris_reg_add(PEAKING_CTRL, peakingctrl); + iris_reg_add(PEAKING_SHADOW_UPDATE, 1); + //mutex_unlock(&iris_cfg->cmd_mutex); + } else { + ret = iris_register_write(mfd, PEAKING_STARTWIN, winstart); + if (ret != 0) + return ret; + ret = iris_register_write(mfd, PEAKING_ENDWIN, winend); + if (ret != 0) + return ret; + ret = iris_register_write(mfd, PEAKING_CTRL, peakingctrl); + if (ret != 0) + return ret; + ret = iris_register_write(mfd, PEAKING_SHADOW_UPDATE, 1); + if (ret != 0) + return ret; + } + } + if (pdemo_win_info->cm_demo_en) { + // FIXME + if (iris_cfg->sf_notify_mode != IRIS_MODE_RFB) { + //mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(CM_STARTWIN, winstart); + iris_reg_add(CM_ENDWIN, winend); + iris_reg_add(CM_SHADOW_UPDATE, 1); + //mutex_unlock(&iris_cfg->cmd_mutex); + } else { + ret = iris_register_write(mfd, CM_STARTWIN, winstart); + if (ret != 0) + return ret; + ret = iris_register_write(mfd, CM_ENDWIN, winend); + if (ret != 0) + return ret; + ret = iris_register_write(mfd, CM_SHADOW_UPDATE, 1); + if (ret != 0) + return ret; + } + } + return 0; +} + +static int iris_configure_get(struct msm_fb_data_type *mfd, u32 type, u32 count, u32 *values) +{ + int ret = 0; + + struct mdss_overlay_private *mdp5_data; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl; + struct quality_setting * pqlt_cur_setting = & iris_info.setting_info.quality_cur; + + mdp5_data = mfd_to_mdp5_data(mfd); + pdata = mdp5_data->ctl->panel_data; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + + if ((type >= IRIS_CONFIG_TYPE_MAX) || (mfd->panel_power_state == MDSS_PANEL_POWER_OFF)) + return -EFAULT; + + ret = irisDsiStsGet(ctrl); + if (ret != IRIS_CONFIGURE_GET_VALUE_CORRECT) + return ret; + + switch (type) { + case IRIS_PEAKING: + *values = pqlt_cur_setting->pq_setting.peaking; + break; + case IRIS_SHARPNESS: + *values = pqlt_cur_setting->pq_setting.sharpness; + break; + case IRIS_MEMC_DEMO: + *values = pqlt_cur_setting->pq_setting.memcdemo; + break; + case IRIS_PEAKING_DEMO: + *values = pqlt_cur_setting->pq_setting.peakingdemo; + break; + case IRIS_GAMMA: + *values = pqlt_cur_setting->pq_setting.gamma; + break; + case IRIS_MEMC_LEVEL: + *values = pqlt_cur_setting->pq_setting.memclevel; + break; + case IRIS_CONTRAST: + *values = pqlt_cur_setting->pq_setting.contrast; + break; + case IRIS_BRIGHTNESS: + *values = pqlt_cur_setting->pq_setting.sharpness; + break; + case IRIS_EXTERNAL_PWM: + *values = pqlt_cur_setting->dbc_setting.ext_pwm; + break; + case IRIS_DBC_QUALITY: + *values = pqlt_cur_setting->dbc_setting.cabcmode; + break; + case IRIS_DLV_SENSITIVITY: + *values = pqlt_cur_setting->dbc_setting.dlv_sensitivity; + break; + case IRIS_DBC_CONFIG: + *values = *((u32 *)&pqlt_cur_setting->dbc_setting); + break; + case IRIS_CINEMA_MODE: + *values = pqlt_cur_setting->pq_setting.cinema_en; + break; + case IRIS_LCE_SETTING: + *values = *((u32 *)&pqlt_cur_setting->lce_setting); + break; + case IRIS_CM_SETTING: + *values = *((u32 *)&pqlt_cur_setting->cm_setting); + break; + case IRIS_COLOR_ADJUST: + *values = pqlt_cur_setting->color_adjust & 0xff; + break; + case IRIS_PQ_CONFIG: + *values = *((u32 *)&pqlt_cur_setting->pq_setting); + break; + case IRIS_LPMEMC_CONFIG: + *values = pqlt_cur_setting->lp_memc_setting.level; + break; + case IRIS_USER_DEMO_WND: + memcpy(values, &iris_demo_win_info, count * sizeof(u32)); + break; + case IRIS_CHIP_VERSION: + *values = IRIS_CHIP_HW_VER; + break; + default: + return -EFAULT; + } + return ret; +} + + +static int iris_set_rotation(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + bool rotationen; + uint32_t top_ctrl0 = 0; + uint32_t value; + + ret = copy_from_user(&value, argp, sizeof(uint32_t)); + if (ret) { + pr_err("can not copy form user %s\n", __func__); + return ret; + } + rotationen = !!(value); + pr_debug("rotationen = %d\n", rotationen); + + top_ctrl0 = (rotationen << ROTATION) | (0 << TRUE_CUT_EXT_EN) + | (0 << INTERVAL_SHIFT_EN) | (1 << PHASE_SHIFT_EN); + mutex_lock(&mfd->iris_conf.cmd_mutex); + iris_reg_add(IRIS_MVC_ADDR + IRIS_MVC_TOP_CTRL0_OFF, top_ctrl0); + iris_reg_add(IRIS_MVC_ADDR + IRIS_MVC_SW_UPDATE_OFF, 1); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + return ret; +} + +static int iris_tx_dsi_mode_switch(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + uint32_t dsi_mode; + + ret = copy_from_user(&dsi_mode, argp, sizeof(uint32_t)); + pr_info("%s, new mode = %d\n", __func__, dsi_mode); + + iris_mipitx_intf_switch_state_reset(mfd, dsi_mode, 1); + return ret; +} + +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) +/***** +* DRC Dynamic resolution change +* +******/ +static int iris_set_drc_size(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + uint32_t utemp; + ret = copy_from_user(&utemp, argp, sizeof(uint32_t)); + + mfd->iris_conf.drc_enable = (utemp > 0) ? true : false; + mfd->iris_conf.drc_size = utemp; + + return ret; +} +#endif + +static int dynamic_fps_set(struct msm_fb_data_type *mfd, void __user *argp) +{ + uint32_t dfps, curr_fps; + int ret = 0; + struct mdss_panel_data *pdata; + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + + ret = copy_from_user(&dfps, argp, sizeof(uint32_t)); + if (ret) { + pr_err("can not copy form user %s\n", __func__); + return ret; + } + + pr_debug("%s: FPS is %d\n", __func__, dfps); + + if (!mdp5_data->ctl || !mdss_mdp_ctl_is_power_on(mdp5_data->ctl)) + return 0; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected for fb%d\n", mfd->index); + return ret; + } + + curr_fps = pdata->panel_info.mipi.frame_rate; + if (dfps == pdata->panel_info.mipi.frame_rate) { + pr_debug("%s: FPS is already %d\n", __func__, dfps); + return ret; + } + mutex_lock(&mdp5_data->dfps_lock); + if (dfps < pdata->panel_info.min_fps) { + pr_debug("Unsupported FPS. min_fps = %d\n", + pdata->panel_info.min_fps); + mutex_unlock(&mdp5_data->dfps_lock); + return ret; + } else if (dfps > pdata->panel_info.max_fps) { + pr_debug("Unsupported FPS. Configuring to max_fps = %d\n", + pdata->panel_info.max_fps); + dfps = pdata->panel_info.max_fps; + pdata->panel_info.new_fps = dfps; + ret = mdss_mdp_ctl_update_fps(mdp5_data->ctl); + } else { + pdata->panel_info.new_fps = dfps; + ret = mdss_mdp_ctl_update_fps(mdp5_data->ctl); + } + if (!ret) { + pr_debug("%s: configured to '%d' FPS\n", __func__, dfps); + } else { + pr_debug("Failed to configure '%d' FPS. rc = %d\n", dfps, ret); + mutex_unlock(&mdp5_data->dfps_lock); + return ret; + } + pdata->panel_info.new_fps = dfps; + mutex_unlock(&mdp5_data->dfps_lock); + + ret = iris_dynamic_fps_set(pdata, curr_fps, dfps); + + return ret; +} + +static int iris_notify_video_frame_rate(struct msm_fb_data_type *mfd, + void __user *argp) +{ + uint32_t r; + int ret = 0; + uint32_t frame_rate_ms; + + ret = copy_from_user(&frame_rate_ms, argp, sizeof(uint32_t)); + if (ret) { + pr_err("copy from user error\n"); + return -EINVAL; + } + pr_info("frame_rate_ms = %u\n", frame_rate_ms); + + // round to integer for 23976 and 29976 + mfd->iris_conf.input_frame_rate = (frame_rate_ms + 100) / 1000; + mfd->iris_conf.output_frame_rate = 60; + + // video mode, no need to set fbo/ratio in/out + if (mfd->panel_info->type == MIPI_VIDEO_PANEL) + return ret; + + r = gcd(mfd->iris_conf.input_frame_rate, mfd->iris_conf.output_frame_rate); + mfd->iris_conf.in_ratio = mfd->iris_conf.input_frame_rate / r; + mfd->iris_conf.out_ratio = mfd->iris_conf.output_frame_rate / r; + pr_debug("%s, in_ratio = %d, out_ratio = %d\n", __func__, mfd->iris_conf.in_ratio, mfd->iris_conf.out_ratio); + + return ret; +} + + +static int msmfb_iris_configure_get(struct msm_fb_data_type *mfd, + uint32_t type, uint32_t count, void __user *argp) +{ + int ret = -1; + uint32_t *val = NULL; + + val = kmalloc(count * sizeof(uint32_t), + GFP_KERNEL); + if (val == NULL) { + pr_err("could not kmalloc space for func = %s\n", __func__); + return -ENOSPC; + } + + ret = iris_configure_get(mfd, type, count, val); + if (ret) { + pr_err("get error\n"); + kfree(val); + return -EPERM; + } + + ret = copy_to_user(argp, + val, sizeof(uint32_t) * count); + if (ret) { + pr_err("copy to user error\n"); + kfree(val); + return -EPERM; + } + + kfree(val); + return ret; +} + + +static int mdss_mipi_dsi_command_t(struct mdss_panel_data *pdata, void __user *argp) +{ + struct mdss_dsi_ctrl_pdata *ctrl; + struct msmfb_mipi_dsi_cmd cmd; + struct dsi_cmd_desc desc = { + .payload = NULL, + }; + struct dsi_cmd_desc *pdesc_muti, *pdesc; + char read_response_buf[16] = {0}; + struct dcs_cmd_req req = { + .cmds = &desc, + .cmds_cnt = 1, + .flags = CMD_REQ_COMMIT | CMD_REQ_NO_MAX_PKT_SIZE, + .rlen = 16, + .rbuf = (char *)&read_response_buf, + .cb = NULL + }; + int ret, indx, cmd_len, cmd_cnt; + char *pcmd_indx; + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + pr_debug("%s:%d: mdss_panel_data: %p mdss_dsi_ctrl_pdata: %p\n", __func__, __LINE__, pdata, ctrl); + ret = copy_from_user(&cmd, argp, sizeof(cmd)); + if (ret) + return ret; + + pr_debug("#### %s:%d vc=%u d=%02x f=%u l=%u\n", __func__, __LINE__, + cmd.vc, cmd.dtype, cmd.flags, cmd.length); + if (cmd.length) { + desc.payload = kmalloc(cmd.length, GFP_KERNEL); + if (!desc.payload) + return -ENOMEM; + ret = copy_from_user(desc.payload, cmd.payload, cmd.length); + if (ret) + goto err; + } + + desc.dchdr.dtype = cmd.dtype; + desc.dchdr.vc = cmd.vc; + desc.dchdr.last = !!(cmd.flags & MSMFB_MIPI_DSI_COMMAND_LAST); + desc.dchdr.ack = !!(cmd.flags & MSMFB_MIPI_DSI_COMMAND_ACK); + desc.dchdr.dlen = cmd.length; + desc.dchdr.wait = 0; + if (cmd.dtype == 0x0f) { + cmd_cnt = *desc.payload; + pdesc_muti = kmalloc(sizeof(struct dsi_cmd_desc) * cmd_cnt, GFP_KERNEL); + pcmd_indx = desc.payload + cmd_cnt + 1; + for (indx = 0; indx < cmd_cnt; indx++) { + pdesc = pdesc_muti + indx; + cmd_len = *(desc.payload + 1 + indx); + pdesc->dchdr.dtype = *pcmd_indx; + pdesc->dchdr.vc = 0; + pdesc->dchdr.last = 0; + pdesc->dchdr.ack = 0; + pdesc->dchdr.dlen = cmd_len - 1; + pdesc->dchdr.wait = 0; + pdesc->payload = pcmd_indx + 1; + + pcmd_indx += cmd_len; + if (indx == (cmd_cnt - 1)) + pdesc->dchdr.last = 1; + printk("dtype:%x, dlen: %d, last: %d\n", pdesc->dchdr.dtype, pdesc->dchdr.dlen, pdesc->dchdr.last); + } + req.cmds = pdesc_muti; + req.cmds_cnt = cmd_cnt; + req.flags = CMD_REQ_COMMIT; + } + + if (cmd.flags & MSMFB_MIPI_DSI_COMMAND_ACK) { + req.flags = req.flags | CMD_REQ_RX; + } + + // This is debug for switch from BFRC Mode directly to PSR Mode + if (cmd.flags & MSMFB_MIPI_DSI_COMMAND_DEBUG) { + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + static char iris_psr_update_cmd[2] = { 0x1, 0x2 }; + struct dsi_cmd_desc iris_psr_update = { + { DTYPE_GEN_WRITE2, 1, 0, 0, 0, + sizeof(iris_psr_update_cmd) }, iris_psr_update_cmd + }; + + iris_wait_for_vsync(mdata->ctl_off); + mdss_dsi_cmd_hs_mode(1, pdata); + mdss_dsi_cmds_tx(ctrl, &iris_psr_update, 1, (CMD_REQ_DMA_TPG & CMD_REQ_COMMIT)); + mdss_dsi_cmd_hs_mode(0, pdata); + } + + if (cmd.flags & MSMFB_MIPI_DSI_COMMAND_BLLP) { + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + iris_wait_for_vsync(mdata->ctl_off); + } + + if (cmd.flags & MSMFB_MIPI_DSI_COMMAND_HS) + mdss_dsi_cmd_hs_mode(1, pdata); + + mdss_dsi_cmdlist_put(ctrl, &req); + + if (cmd.flags & MSMFB_MIPI_DSI_COMMAND_HS) + mdss_dsi_cmd_hs_mode(0, pdata); + + if (ctrl->rx_buf.data) { + memcpy(cmd.response, ctrl->rx_buf.data, sizeof(cmd.response)); + } + ret = copy_to_user(argp, &cmd, sizeof(cmd)); +err: + kfree(desc.payload); + if (cmd.dtype == 0x0f) + kfree(pdesc_muti); + return ret; +} + + +static int mdss_mipi_dsi_command(struct msm_fb_data_type *mfd, void __user *argp) +{ + struct mdss_overlay_private *mdp5_data = NULL; + + mdp5_data = mfd_to_mdp5_data(mfd); + if (!mdp5_data) { + pr_err("mdp5 data is null\n"); + return -EINVAL; + } + + return mdss_mipi_dsi_command_t(mdp5_data->ctl->panel_data, argp); +} + + +static void dump_iris_i2c_oprt(struct msmfd_iris_i2c_cmd *i2c_cmd_ptr) +{ + int i = 0; + for (i = 0; i < i2c_cmd_ptr->addr_comp_len; i++) { + switch (i2c_cmd_ptr->type) { + case MSMFB_IRIS_I2C_READ: + pr_debug("[addr = %08x]\n", i2c_cmd_ptr->addr_comp[i].addr); + break; + case MSMFB_IRIS_I2C_WRITE: + pr_debug("[addr = %08x, val = %08x]\n" , + i2c_cmd_ptr->addr_comp[i].addr, i2c_cmd_ptr->addr_comp[i].data); + break; + } + } +} + +static int iris_i2c_oprt(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret = -1; + int len = 0; + int ret_val = -1; + int i = 0; + struct msmfd_iris_i2c_cmd i2c_cmd; + struct msmfd_iris_i2c_cmd *i2c_cmd_ptr = (struct msmfd_iris_i2c_cmd *)argp; + + if (mfd && mfd->panel_power_state == MDSS_PANEL_POWER_OFF) { + pr_err("panel is power off\n"); + return 0; + } + + memset(&i2c_cmd, 0x00, sizeof(i2c_cmd)); + ret_val = copy_from_user(&i2c_cmd, argp, sizeof(i2c_cmd)); + if (ret_val) { + pr_err("could not copy from user %d\n", ret_val); + return ret_val; + } + + len = i2c_cmd.addr_comp_len; + if (len == 0) { + pr_err("no addr is setting\n"); + return -EACCES; + } + + //dump_iris_i2c_oprt(&i2c_cmd); + + mutex_lock(&mfd->iris_conf.cmd_mutex); + switch (i2c_cmd.type) { + case MSMFB_IRIS_I2C_READ: + ret = iris_i2c_read(i2c_cmd.addr_comp, i2c_cmd.addr_comp_len); + if (ret) { + pr_err("the read addr is failed %d\n", ret); + dump_iris_i2c_oprt(&i2c_cmd); + } + break; + case MSMFB_IRIS_I2C_WRITE: + ret = iris_i2c_write(i2c_cmd.addr_comp, i2c_cmd.addr_comp_len); + if (ret) { + pr_err("the write addr is failed %d\n", ret); + dump_iris_i2c_oprt(&i2c_cmd); + } + break; + } + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + if (ret == 0 && i2c_cmd.type == MSMFB_IRIS_I2C_READ) { + for (i = 0; i < i2c_cmd.addr_comp_len; i++) { + ret = copy_to_user(&i2c_cmd_ptr->addr_comp[i].data, + &i2c_cmd.addr_comp[i].data, sizeof(i2c_cmd.addr_comp[i].data)); + if (ret) { + pr_err("could not copy to user %d\n", ret); + return ret; + } + } + } + return ret_val; +} + + +static int iris_configure_ex_t(struct msm_fb_data_type *mfd, uint32_t type, + uint32_t count, void __user *values) +{ + int ret = -1; + uint32_t *val = NULL; + + val = kmalloc(sizeof(uint32_t) * count, GFP_KERNEL); + if (!val) { + pr_err("can not kmalloc space\n"); + return -ENOSPC; + } + ret = copy_from_user(val, values, sizeof(uint32_t) * count); + if (ret) { + kfree(val); + return ret; + } + + ret = iris_configure_ex(mfd, type, count, val); + + kfree(val); + return ret; +} + +static int iris_set_dport_writeback_skip(struct msm_fb_data_type *mfd, + void __user *argp) +{ + int ret; + int skipCnt; + ATRACE_BEGIN(__func__); + ret = copy_from_user(&skipCnt, argp, sizeof(bool)); + mutex_lock(&mfd->iris_conf.cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x134c, 0); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + pr_debug("disable iris Dport write back!n"); + ATRACE_END(__func__); + + return ret; +} + +static int iris_configure_t(struct msm_fb_data_type *mfd, u32 type, void __user *argp) +{ + int ret = -1; + uint32_t value = 0; + ret = copy_from_user(&value, argp, sizeof(uint32_t)); + if (ret) + return ret; + ret = iris_configure(mfd, type, value); + return ret; +} + + +int msmfb_iris_operate_conf(struct msm_fb_data_type *mfd, + void __user *argp) +{ + int ret = -1; + uint32_t parent_type = 0; + uint32_t child_type = 0; + struct msmfb_iris_operate_value configure; + + ret = copy_from_user(&configure, argp, sizeof(configure)); + if (ret) + return ret; + + pr_debug("%s type = %d, value = %d\n", + __func__, configure.type, configure.count); + + child_type = (configure.type >> 8) & 0xff; + parent_type = configure.type & 0xff; + + switch (parent_type) { + case IRIS_OPRT_ROTATION_SET: + ret = iris_set_rotation(mfd, configure.values); + break; + case IRIS_OPRT_VIDEO_FRAME_RATE_SET: + ret = iris_notify_video_frame_rate(mfd, configure.values); + break; + case IRIS_OPRT_CONFIGURE: + ret = iris_configure_t(mfd, child_type, configure.values); + break; + case IRIS_OPRT_CONFIGURE_NEW: + ret = iris_configure_ex_t(mfd, child_type, + configure.count, configure.values); + break; + case IRIS_OPRT_CONFIGURE_NEW_GET: + ret = msmfb_iris_configure_get(mfd, child_type, + configure.count, configure.values); + break; + case IRIS_OPRT_DPORT_WRITEBACK_SKIP: + ret = iris_set_dport_writeback_skip(mfd, configure.values); + break; + case IRIS_OPRT_MIPITX_MODESWITCH: + ret = iris_tx_dsi_mode_switch(mfd, configure.values); + break; +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + case IRIS_OPRT_GET_FRCTIMING: + ret = iris_get_frc_timing(mfd, configure.values); + break; + case IRIS_OPRT_SET_DRC_SIZE: + ret = iris_set_drc_size(mfd, configure.values); + break; +#endif + case IRIS_OPRT_GET_AVAILABLE_MODE: + ret = iris_get_available_mode(mfd, ((struct msmfb_iris_operate_value*)argp)->values); + break; + default: + pr_err("could not find right opertat type = %d\n", configure.type); + break; + } + return ret; +} + +static int iris_set_mode(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + uint32_t mode; + struct iris_config *iris_cfg = &(mfd->iris_conf); + + ret = copy_from_user(&mode, argp, sizeof(uint32_t)); + + pr_info("iris_set_mode: new mode = %d, old mode = %d\n", + mode, iris_cfg->sf_notify_mode); + + if (mode != iris_cfg->sf_notify_mode) + { + iris_cfg->sf_mode_change_start = true; + iris_cfg->sf_notify_mode = mode; + } + return ret; +} + +static int iris_get_mode(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + uint32_t mode; + struct iris_config *iris_cfg = &(mfd->iris_conf); + + mode = iris_cfg->sf_notify_mode; + pr_debug("mode = %d\n", iris_cfg->sf_notify_mode); + ret = copy_to_user(argp, &mode, sizeof(uint32_t)); + + return ret; +} + +int iris_operate_mode(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret = -1; + struct msmfb_iris_operate_value val; + ret = copy_from_user(&val, argp, sizeof(val)); + if (ret != 0) { + pr_err("can not copy from user\n"); + return -EPERM; + } + + if (val.type == IRIS_OPRT_MODE_SET) { + ret = iris_set_mode(mfd, val.values); + } else { + ret = iris_get_mode(mfd, val.values); + } + return ret; +} + +int iris_set_meta(struct msm_fb_data_type *mfd, void __user *argp) +{ + int ret; + struct iris_config *iris_cfg = &mfd->iris_conf; + struct iris_meta user_meta; + + ret = copy_from_user((void *)&user_meta, argp, sizeof(struct iris_meta)); + if (ret == 0) { + mutex_lock(&iris_cfg->meta_mutex); + iris_cfg->meta_set.op |= user_meta.op; + if (user_meta.op & MDP_IRIS_OP_NEW_FRAME) + iris_cfg->meta_set.new_frame = user_meta.new_frame; + if (user_meta.op & MDP_IRIS_OP_RESTART) + iris_cfg->meta_set.restart = user_meta.restart; + if (user_meta.op & MDP_IRIS_OP_VTS) + iris_cfg->meta_set.video_ts = user_meta.video_ts; + if (user_meta.op & MDP_IRIS_OP_STS) + iris_cfg->meta_set.sys_ts = user_meta.sys_ts; + if (user_meta.op & MDP_IRIS_OP_VID) + iris_cfg->meta_set.vid = user_meta.vid; + if (user_meta.op & MDP_IRIS_OP_TE) + iris_cfg->meta_set.te_period = user_meta.te_period; + if (user_meta.op & MDP_IRIS_OP_CP) { + iris_cfg->meta_set.content_period = user_meta.content_period; + iris_cfg->meta_set.content_period_frac = user_meta.content_period_frac; + } + if (user_meta.op & MDP_IRIS_OP_MOTION) + iris_cfg->meta_set.motion = user_meta.motion; + if (user_meta.op & MDP_IRIS_OP_JITTER) + iris_cfg->meta_set.jitter = user_meta.jitter; + if (user_meta.op & MDP_IRIS_OP_NRV) + iris_cfg->meta_set.nrv = user_meta.nrv; + if (user_meta.op & MDP_IRIS_OP_FLG) + iris_cfg->meta_set.flags = user_meta.flags; + if (user_meta.op & MDP_IRIS_OP_RPT) + iris_cfg->meta_set.repeat = user_meta.repeat; + if (user_meta.op & MDP_IRIS_OP_IF1) + iris_cfg->meta_set.iris_info1 = user_meta.iris_info1; + if (user_meta.op & MDP_IRIS_OP_IF2) + iris_cfg->meta_set.iris_info2 = user_meta.iris_info2; + mutex_unlock(&iris_cfg->meta_mutex); + } + + pr_debug("op [%08x] vTimestamp [%u] sTimestamp [%u] flag [%u]\n", + iris_cfg->meta_set.op, iris_cfg->meta_set.video_ts, iris_cfg->meta_set.sys_ts, iris_cfg->meta_set.flags); + + if (iris_cfg->meta_set.op & MDP_IRIS_OP_RPT) + pr_debug("repeat: %d\n", iris_cfg->meta_set.repeat); + if (iris_cfg->meta_set.op & MDP_IRIS_OP_NRV) { + struct iris_nrv_meta *nrv_meta = &iris_cfg->meta_set.nrv; + pr_debug("NRV enable [%u]\n", nrv_meta->nrvEnable); + pr_debug("Capture [%u][%u] [%u][%u]\n", nrv_meta->captureLeft, nrv_meta->captureRight, + nrv_meta->captureTop, nrv_meta->captureBottom); + pr_debug("Display [%u][%u] [%u][%u]\n", nrv_meta->displayLeft, nrv_meta->displayRight, + nrv_meta->displayTop, nrv_meta->displayBottom); + } + return ret; +} + + +int msmfb_iris_operate_tool(struct msm_fb_data_type *mfd, + void __user *argp) +{ + int ret = -1; + uint32_t parent_type = 0; + struct msmfb_iris_operate_value configure; + + ret = copy_from_user(&configure, argp, sizeof(configure)); + if (ret) + return ret; + + pr_debug("%s type = %d, value = %d\n", + __func__, configure.type, configure.count); + + parent_type = configure.type & 0xff; + switch (parent_type) { + case IRIS_OPRT_TOOL_I2C: + ret = iris_i2c_oprt(mfd, configure.values); + break; + case IRIS_OPRT_TOOL_DSI: + ret = mdss_mipi_dsi_command(mfd, configure.values); + break; + case IRIS_OPRT_DYNAMIC_FPS_SET: + ret = dynamic_fps_set(mfd, configure.values); + break; + default: + pr_err("could not find right opertat type = %d\n", configure.type); + break; + } + return ret; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.h new file mode 100644 index 000000000000..6cb8df3563d9 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_ioctl.h @@ -0,0 +1,22 @@ +#ifndef MDSS_DSI_IRIS_IOCTL +#define MDSS_DSI_IRIS_IOCTL + +enum res_ratio{ + ratio_4to3 = 1, + ratio_16to10, + ratio_16to9, +}; + +int msmfb_iris_operate_conf(struct msm_fb_data_type *mfd, + void __user *argp); + +int iris_operate_mode(struct msm_fb_data_type *mfd, + void __user *argp); + +int iris_set_meta(struct msm_fb_data_type *mfd, void __user *argp); + + +int msmfb_iris_operate_tool(struct msm_fb_data_type *mfd, + void __user *argp); + +#endif diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.c new file mode 100644 index 000000000000..173e8867fe03 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.c @@ -0,0 +1,2312 @@ +/* Copyright (c) 2013, Pixelworks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_fb.h" + +#include "mdss_dsi.h" +#include "mdss_dsi_iris2p_lightup_priv.h" +#include "mdss_dsi_iris2p_extern.h" +#include "mdss_dsi_iris2p_mode_switch.h" +#include "mdss_dsi_iris2p.h" + +static u8 iris_power_mode; +static u8 iris_signal_mode; +//static u16 iris_mipirx_status; +char iris_read_cmd_buf[16]; + +static char grcp_header[GRCP_HEADER] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x3), + 0x00, + 0x00, + PWIL_U16(0x2), +}; +static struct iris_grcp_cmd init_cmd[INIT_CMD_NUM]; +static struct iris_grcp_cmd grcp_cmd; +//static struct msm_fb_data_type *gp_mfd; +static struct mdss_dsi_ctrl_pdata *g_ctrl; + +//#define DUMP_DATA_FOR_BOOTLOADER + +#ifdef DUMP_DATA_FOR_BOOTLOADER +void iris_dump_packet(u8 *data, int size) +{ + int i = 0; + pr_err("size = %d\n", size); + for (i = 0; i < size; i += 4) + pr_err("0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", + *(data+i), *(data+i+1), *(data+i+2), *(data+i+3)); +} +#define DUMP_PACKET iris_dump_packet +#else +#define DUMP_PACKET(...) +#endif + +static void iris_mipi_signal_mode_cb(int len) +{ + if (len != 1) { + pr_err("%s: not short read responese, return len [%02x] != 1\n", __func__, len); + return; + } + iris_signal_mode = (u8)iris_read_cmd_buf[0]; + pr_info("signal mode [%02x]\n", iris_signal_mode); +} + +u8 iris_mipi_signal_mode_read(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + struct dcs_cmd_req cmdreq; + char get_signal_mode[1] = {0x0e}; + struct dsi_cmd_desc iris_signal_mode_cmd = { + {DTYPE_DCS_READ, 1, 0, 1, 0, sizeof(get_signal_mode)}, get_signal_mode}; + + memset(&cmdreq, 0, sizeof(cmdreq)); + iris_signal_mode = 0xff; + memset(iris_read_cmd_buf, 0, sizeof(iris_read_cmd_buf)); + + cmdreq.cmds = &iris_signal_mode_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + if (DSI_HS_MODE == state) + cmdreq.flags |= CMD_REQ_HS_MODE; + cmdreq.rlen = 1; + cmdreq.rbuf = iris_read_cmd_buf; + cmdreq.cb = iris_mipi_signal_mode_cb; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + return iris_signal_mode; +} + + +static void iris_mipi_power_mode_cb(int len) +{ + if (len != 1) { + pr_err("%s: not short read responese, return len [%02x] != 1\n", __func__, len); + return; + } + iris_power_mode = (u8)iris_read_cmd_buf[0]; + pr_info("power mode [%02x]\n", iris_power_mode); +} + +u8 iris_mipi_power_mode_read(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + struct dcs_cmd_req cmdreq; + char get_power_mode[1] = {0x0a}; + struct dsi_cmd_desc iris_power_mode_cmd = { + {DTYPE_DCS_READ, 1, 0, 1, 0, sizeof(get_power_mode)}, get_power_mode}; + + memset(&cmdreq, 0, sizeof(cmdreq)); + iris_power_mode = 0; + memset(iris_read_cmd_buf, 0, sizeof(iris_read_cmd_buf)); + + cmdreq.cmds = &iris_power_mode_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + if (DSI_HS_MODE == state) + cmdreq.flags |= CMD_REQ_HS_MODE; + cmdreq.rlen = 1; + cmdreq.rbuf = iris_read_cmd_buf; + cmdreq.cb = iris_mipi_power_mode_cb; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + return iris_power_mode; +} + + +static void iris_dsi_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + + if (pcmds->link_state == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +int iris_power_mode_check(struct mdss_dsi_ctrl_pdata *ctrl, u8 value, int state) +{ +#define RETRY_TIMES 5 + u8 cnt = 0, powermode = 0; + + do { + powermode = iris_mipi_power_mode_read(ctrl, state); + if (powermode == value) + break; + + msleep(5); + cnt++; + } while ((powermode != value) && cnt < RETRY_TIMES); + + /* read failed */ + if (cnt == RETRY_TIMES) { + pr_err("power mode check %x failed\n", value); + return FAILED; + } else + pr_info("power mode check %x success\n", value); + + return SUCCESS; +} + + +int iris_proxy_check_reset(struct mdss_dsi_ctrl_pdata *ctrl) +{ +#define REBOOT_TIMES 5 + u8 cnt = 0, powermode = 0; + int i; + + struct mdss_panel_info *panel_info = &(ctrl->panel_data.panel_info); + + do { + powermode = iris_mipi_power_mode_read(ctrl, DSI_LP_MODE); + + pr_debug("read back powermode: %d, cnt: %d\n", powermode, cnt); + + if (powermode == 0x01) + break; + + for (i = 0; i < panel_info->rst_seq_len; ++i) { + gpio_set_value((ctrl->rst_gpio), + panel_info->rst_seq[i]); + if (panel_info->rst_seq[++i]) + usleep_range(panel_info->rst_seq[i] * 1000, panel_info->rst_seq[i] * 1000); + } + cnt++; + } while ((powermode != 0x01) && cnt < REBOOT_TIMES); + + /* read failed */ + if (cnt == REBOOT_TIMES) { + pr_err("reboot workaround, power mode check failed\n"); + return FAILED; + } else + pr_info("reboot times, succeed, power mode check success\n"); + + return SUCCESS; + +} + +void iris_workmode_parse(struct device_node *np, + struct mdss_panel_info *panel_info, struct iris_info_t *piris_info) +{ + int rc; + u32 iris_rx_ch = 1, iris_tx_ch = 1, iris_rx_dsc = 0, iris_tx_dsc = 0, iris_tx_mode = MIPI_VIDEO_MODE; + u32 tmp, iris_rx_pxl_mod = 0, iris_tx_pxl_mod = 1; + struct iris_work_mode *pwork_mode = &(piris_info->work_mode); + const char *data; + + data = of_get_property(np, "qcom,iris-mipitx-type", NULL); + if (data && !strncmp(data, "dsi_cmd_mode", 12)) + iris_tx_mode = MIPI_CMD_MODE; + else + iris_tx_mode = MIPI_VIDEO_MODE; + + rc = of_property_read_u32(np, "qcom,iris-mipirx-channel", &tmp); + iris_rx_ch = (!rc ? tmp : 1); + rc = of_property_read_u32(np, "qcom,iris-mipitx-channel", &tmp); + iris_tx_ch = (!rc ? tmp : 1); + rc = of_property_read_u32(np, "qcom,iris-mipirx-dsc", &tmp); + iris_rx_dsc = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,iris-mipitx-dsc", &tmp); + iris_tx_dsc = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,iris-mipirx-pxl-mode", &tmp); + iris_rx_pxl_mod = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,iris-mipitx-pxl-mode", &tmp); + iris_tx_pxl_mod = (!rc ? tmp : 1); + + /*iris mipirx mode*/ + pwork_mode->rx_mode = (DSI_VIDEO_MODE == panel_info->mipi.mode) ? MIPI_VIDEO_MODE : MIPI_CMD_MODE; + pwork_mode->rx_ch = (iris_rx_ch == 1) ? 0 : 1; + pwork_mode->rx_dsc = iris_rx_dsc; + pwork_mode->rx_pxl_mode = iris_rx_pxl_mod; + /*iris mipitx mode*/ + pwork_mode->tx_mode = iris_tx_mode; + pwork_mode->tx_ch = (iris_tx_ch == 1) ? 0 : 1; + pwork_mode->tx_dsc = iris_tx_dsc; + pwork_mode->tx_pxl_mode = iris_tx_pxl_mod; + +} + +void iris_timing_parse(struct device_node *np, + struct mdss_panel_info *panel_info, struct iris_info_t *piris_info) +{ + int rc = 0; + u32 tmp; + struct iris_timing_info *pinput_timing = &(piris_info->input_timing); + struct iris_timing_info *poutput_timing = &(piris_info->output_timing); + + pinput_timing->hfp = panel_info->lcdc.h_front_porch; + pinput_timing->hres = panel_info->xres; + pinput_timing->hbp = panel_info->lcdc.h_back_porch; + pinput_timing->hsw = panel_info->lcdc.h_pulse_width; + + pinput_timing->vfp = panel_info->lcdc.v_front_porch; + pinput_timing->vres = panel_info->yres; + pinput_timing->vbp = panel_info->lcdc.v_back_porch; + pinput_timing->vsw = panel_info->lcdc.v_pulse_width; + pinput_timing->fps = panel_info->mipi.frame_rate; + + rc = of_property_read_u32(np, "qcom,iris-out-panel-width", &tmp); + if (rc) { + /*copy input timing to output timing*/ + memcpy(poutput_timing, pinput_timing, sizeof(struct iris_timing_info)); + } else { + /*parse output timing*/ + poutput_timing->hres = (!rc ? tmp : 640); + + rc = of_property_read_u32(np, "qcom,iris-out-panel-height", &tmp); + poutput_timing->vres = (!rc ? tmp : 480); + rc = of_property_read_u32(np, "qcom,iris-out-h-front-porch", &tmp); + poutput_timing->hfp = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,iris-out-h-back-porch", &tmp); + poutput_timing->hbp = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,iris-out-h-pulse-width", &tmp); + poutput_timing->hsw = (!rc ? tmp : 2); + rc = of_property_read_u32(np, "qcom,iris-out-v-back-porch", &tmp); + poutput_timing->vbp = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,iris-out-v-front-porch", &tmp); + poutput_timing->vfp = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,iris-out-v-pulse-width", &tmp); + poutput_timing->vsw = (!rc ? tmp : 2); + rc = of_property_read_u32(np, "qcom,iris-out-framerate", &tmp); + poutput_timing->fps = (!rc ? tmp : 60); + } + +} + +void iris_dsc_info_parse(struct device_node *np, + struct mdss_panel_info *panel_info, struct iris_info_t *piris_info) +{ + int rc = 0; + u32 tmp; + struct iris_dsc_info *pinput_dsc = &(piris_info->input_dsc); + struct iris_dsc_info *poutput_dsc = &(piris_info->output_dsc); + + /*parse input DSC para*/ + rc = of_property_read_u32(np, "qcom,iris-in-slice-number", &tmp); + pinput_dsc->slice_number = (!rc ? tmp : 8); + rc = of_property_read_u32(np, "qcom,iris-in-slice-height", &tmp); + pinput_dsc->slice_height = (!rc ? tmp : 16); + rc = of_property_read_u32(np, "qcom,iris-in-bpp", &tmp); + pinput_dsc->bpp = (!rc ? tmp : 0x80); + + /*parse output DSC para*/ + rc = of_property_read_u32(np, "qcom,iris-in-slice-number", &tmp); + poutput_dsc->slice_number = (!rc ? tmp : 8); + rc = of_property_read_u32(np, "qcom,iris-in-slice-height", &tmp); + poutput_dsc->slice_height = (!rc ? tmp : 16); + rc = of_property_read_u32(np, "qcom,iris-in-bpp", &tmp); + poutput_dsc->bpp = (!rc ? tmp : 0x80); +} + +void iris_setting_info_parse(struct device_node *np, + struct mdss_panel_info *panel_info, struct iris_info_t *piris_info) +{ + int rc = 0; + u32 tmp; + struct device_node *settings_node = NULL; + struct iris_setting_info *psetting_info = &(piris_info->setting_info); + + rc = of_property_read_u32(np, "qcom,iris-dpll-clk", &tmp); + psetting_info->dpll_clock = (!rc ? tmp : 26664280); + rc = of_property_read_u32(np, "qcom,iris-dpll0", &tmp); + psetting_info->dpll0 = (!rc ? tmp : 0xa); + rc = of_property_read_u32(np, "qcom,iris-dpll1", &tmp); + psetting_info->dpll1 = (!rc ? tmp : 0x143b01); + rc = of_property_read_u32(np, "qcom,iris-dpll2", &tmp); + psetting_info->dpll2 = (!rc ? tmp : 0xbd2788); + rc = of_property_read_u32(np, "qcom,iris-ppll1", &tmp); + psetting_info->ppll1 = (!rc ? tmp : 0x341101); + rc = of_property_read_u32(np, "qcom,iris-ppll2", &tmp); + psetting_info->ppll2 = (!rc ? tmp : 0x155555); + rc = of_property_read_u32(np, "qcom,iris-tx-escclk-setting", &tmp); + psetting_info->tx_escclk_setting = (!rc ? tmp : 0x10); + + rc = of_property_read_u32(np, "qcom,mipirx-dsi-functional-program", &tmp); + psetting_info->mipirx_dsi_functional_program = (!rc ? tmp : 0x62); + rc = of_property_read_u32(np, "qcom,mipirx-eot-ecc-crc-disable", &tmp); + psetting_info->mipirx_eot_ecc_crc_disable = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mipirx-data-lane-timing-param", &tmp); + psetting_info->mipirx_data_lane_timing_param = (!rc ? tmp : 0xff04); + + rc = of_property_read_u32(np, "qcom,mipitx-dsi-tx-ctrl", &tmp); + psetting_info->mipitx_dsi_tx_ctrl = (!rc ? tmp : 0x0a004035); + rc = of_property_read_u32(np, "qcom,mipitx-hs-tx-timer", &tmp); + psetting_info->mipitx_hs_tx_timer = (!rc ? tmp : 0x00ffffff); + rc = of_property_read_u32(np, "qcom,mipitx-bta-lp-timer", &tmp); + psetting_info->mipitx_bta_lp_timer = (!rc ? tmp : 0x00ffff17); + rc = of_property_read_u32(np, "qcom,mipitx-initialization-reset-timer", &tmp); + psetting_info->mipitx_initialization_reset_timer = (!rc ? tmp : 0x0a8c07d0); + rc = of_property_read_u32(np, "qcom,mipitx-dphy-timing-margin", &tmp); + psetting_info->mipitx_dphy_timing_margin = (!rc ? tmp : 0x00040401); + rc = of_property_read_u32(np, "qcom,mipitx-lp-timing-para", &tmp); + psetting_info->mipitx_lp_timing_para = (!rc ? tmp : 0x1003000a); + rc = of_property_read_u32(np, "qcom,mipitx-data-lane-timing-param1", &tmp); + psetting_info->mipitx_data_lane_timing_param = (!rc ? tmp : 0x6030600); + rc = of_property_read_u32(np, "qcom,mipitx-clock-lane-timing-param", &tmp); + psetting_info->mipitx_clk_lane_timing_param = (!rc ? tmp : 0x07030d03); + rc = of_property_read_u32(np, "qcom,mipitx-dphy-pll-para", &tmp); + psetting_info->mipitx_dphy_pll_para = (!rc ? tmp : 0x000007d0); + rc = of_property_read_u32(np, "qcom,mipitx-dphy-trim-1", &tmp); + psetting_info->mipitx_dphy_trim_1 = (!rc ? tmp : 0xedb5384c); + + /*parse delta period for avsync*/ + rc = of_property_read_u32(np, "qcom,iris-delta-period-max", &tmp); + psetting_info->delta_period_max = (!rc ? tmp : piris_info->output_timing.vfp); + rc = of_property_read_u32(np, "qcom,iris-delta-period-min", &tmp); + psetting_info->delta_period_min = (!rc ? (0 - tmp) : (0 - piris_info->output_timing.vfp)); + + /*from root to find pxlw,mdss_iris_settings**/ + settings_node = of_find_node_by_name(NULL, "pxlw,mdss_iris_settings"); + if (settings_node) { + struct iris_setting_disable_info * pdisable_info = &psetting_info->disable_info; + + rc = of_property_read_u32(settings_node, "pxlw,iris-last-frame-repeat-cnt", &tmp); + pdisable_info->last_frame_repeat_cnt = (!rc ? tmp : 2); + + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-dbc-dlv-sensitivity", &tmp); + pdisable_info->dbc_dlv_sensitivity_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-dbc-quality", &tmp); + pdisable_info->dbc_quality_disable_val = (!rc ? tmp : 0); + + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-pq-peaking", &tmp); + pdisable_info->pq_peaking_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-pq-peaking-demo", &tmp); + pdisable_info->pq_peaking_demo_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-pq-gamma", &tmp); + pdisable_info->pq_gamma_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-pq-contrast", &tmp); + pdisable_info->pq_contrast_disable_val = (!rc ? tmp : 50); + + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-color-adjust", &tmp); + pdisable_info->color_adjust_disable_val = (!rc ? tmp : 0); + + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-lce", &tmp); + pdisable_info->lce_mode_disable_val = (!rc ? tmp : 0); + + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-cm-c6axes", &tmp); + pdisable_info->cm_c6axes_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-cm-c3d", &tmp); + pdisable_info->cm_c3d_disable_val = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-disable-cm-fleshtone", &tmp); + pdisable_info->cm_ftcen_disable_val = (!rc ? tmp : 0); + + /* init APP code default value */ + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-peaking-default", &tmp); + psetting_info->quality_def.pq_setting.peaking = (!rc ? tmp : 1); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-sharpness-default", &tmp); + psetting_info->quality_def.pq_setting.sharpness = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-peaking-demo-mode-default", &tmp); + psetting_info->quality_def.pq_setting.peakingdemo = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-memc-demo-mode-default", &tmp); + psetting_info->quality_def.pq_setting.memcdemo = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-memclevel-default", &tmp); + psetting_info->quality_def.pq_setting.memclevel = (!rc ? tmp : 3); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-gamma-mode-default", &tmp); + psetting_info->quality_def.pq_setting.gamma = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-contrast-default", &tmp); + psetting_info->quality_def.pq_setting.contrast = (!rc ? tmp : 0x32); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-cinema-default", &tmp); + psetting_info->quality_def.pq_setting.cinema_en = (!rc ? tmp : 0x00); + + rc = of_property_read_u32(settings_node, "pxlw,iris-cabcmode-default", &tmp); + psetting_info->quality_def.dbc_setting.cabcmode = (!rc ? tmp : 1); + + rc = of_property_read_u32(settings_node, "pxlw,iris-color-adjust-default", &tmp); + psetting_info->quality_def.color_adjust = (!rc ? tmp : 0x32); + + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode-default", &tmp); + psetting_info->quality_def.lce_setting.mode = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode1-level-default", &tmp); + psetting_info->quality_def.lce_setting.mode1level = (!rc ? tmp : 0x01); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode2-level-default", &tmp); + psetting_info->quality_def.lce_setting.mode2level = (!rc ? tmp : 0x01); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-demo-mode-default", &tmp); + psetting_info->quality_def.lce_setting.demomode = (!rc ? tmp : 0x00); + + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-6axes-default", &tmp); + psetting_info->quality_def.cm_setting.cm6axes = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-3d-default", &tmp); + psetting_info->quality_def.cm_setting.cm3d = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-demo-mode-default", &tmp); + psetting_info->quality_def.cm_setting.demomode = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-fleshtone-default", &tmp); + psetting_info->quality_def.cm_setting.ftc_en = (!rc ? tmp : 0x00); + + /* init AP default value */ + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-peaking-init", &tmp); + psetting_info->quality_cur.pq_setting.peaking = (!rc ? tmp : 1); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-sharpness-init", &tmp); + psetting_info->quality_cur.pq_setting.sharpness = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-peaking-demo-mode-init", &tmp); + psetting_info->quality_cur.pq_setting.peakingdemo = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-memc-demo-mode-init", &tmp); + psetting_info->quality_cur.pq_setting.memcdemo = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-memclevel-init", &tmp); + psetting_info->quality_cur.pq_setting.memclevel = (!rc ? tmp : 3); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-gamma-mode-init", &tmp); + psetting_info->quality_cur.pq_setting.gamma = (!rc ? tmp : 0); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-contrast-init", &tmp); + psetting_info->quality_cur.pq_setting.contrast = (!rc ? tmp : 0x32); + rc = of_property_read_u32(settings_node, "pxlw,iris-pq-cinema-init", &tmp); + psetting_info->quality_cur.pq_setting.cinema_en = (!rc ? tmp : 0x00); + + rc = of_property_read_u32(settings_node, "pxlw,iris-cabcmode-init", &tmp); + psetting_info->quality_cur.dbc_setting.cabcmode = (!rc ? tmp : 1); + + rc = of_property_read_u32(settings_node, "pxlw,iris-color-adjust-init", &tmp); + psetting_info->quality_cur.color_adjust = (!rc ? tmp : 0x32); + + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode-init", &tmp); + psetting_info->quality_cur.lce_setting.mode = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode1-level-init", &tmp); + psetting_info->quality_cur.lce_setting.mode1level = (!rc ? tmp : 0x01); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-mode2-level-init", &tmp); + psetting_info->quality_cur.lce_setting.mode2level = (!rc ? tmp : 0x01); + rc = of_property_read_u32(settings_node, "pxlw,iris-lce-demo-mode-init", &tmp); + psetting_info->quality_cur.lce_setting.demomode = (!rc ? tmp : 0x00); + + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-6axes-init", &tmp); + psetting_info->quality_cur.cm_setting.cm6axes = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-3d-init", &tmp); + psetting_info->quality_cur.cm_setting.cm3d = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-demo-mode-init", &tmp); + psetting_info->quality_cur.cm_setting.demomode = (!rc ? tmp : 0x00); + rc = of_property_read_u32(settings_node, "pxlw,iris-cm-fleshtone-init", &tmp); + psetting_info->quality_cur.cm_setting.ftc_en = (!rc ? tmp : 0x00); + } else { + pr_err("could not find pxlw,mdss_iris_settings child\n"); + } +} + +int iris_dsi_parse_dcs_cmds(struct device_node *np, + struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) +{ + const char *data; + int blen = 0, len; + char *buf, *bp; + struct dsi_ctrl_hdr *dchdr; + int i, cnt; + + data = of_get_property(np, cmd_key, &blen); + if (!data) { + pr_err("%s: failed, key=%s\n", __func__, cmd_key); + return -ENOMEM; + } + + buf = kzalloc(sizeof(char) * blen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, blen); + + /* scan dcs commands */ + bp = buf; + len = blen; + cnt = 0; + while (len >= sizeof(*dchdr)) { + dchdr = (struct dsi_ctrl_hdr *)bp; + dchdr->dlen = ntohs(dchdr->dlen); + if (dchdr->dlen > len) { + pr_err("%s: dtsi cmd=%x error, len=%d", + __func__, dchdr->dtype, dchdr->dlen); + goto exit_free; + } + bp += sizeof(*dchdr); + len -= sizeof(*dchdr); + bp += dchdr->dlen; + len -= dchdr->dlen; + cnt++; + } + + if (len != 0) { + pr_err("%s: dcs_cmd=%x len=%d error!", + __func__, buf[0], blen); + goto exit_free; + } + + pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc), + GFP_KERNEL); + if (!pcmds->cmds) + goto exit_free; + + pcmds->cmd_cnt = cnt; + pcmds->buf = buf; + pcmds->blen = blen; + + bp = buf; + len = blen; + for (i = 0; i < cnt; i++) { + dchdr = (struct dsi_ctrl_hdr *)bp; + len -= sizeof(*dchdr); + bp += sizeof(*dchdr); + pcmds->cmds[i].dchdr = *dchdr; + pcmds->cmds[i].payload = bp; + bp += dchdr->dlen; + len -= dchdr->dlen; + } + + /*Set default link state to LP Mode*/ + pcmds->link_state = DSI_LP_MODE; + + if (link_key) { + data = of_get_property(np, link_key, NULL); + if (data && !strcmp(data, "dsi_hs_mode")) + pcmds->link_state = DSI_HS_MODE; + else + pcmds->link_state = DSI_LP_MODE; + } + + pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__, + pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state); + + return 0; + +exit_free: + kfree(buf); + return -ENOMEM; +} + +void iris_on_cmds_parse(struct device_node *np, struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (MIPI_VIDEO_MODE == iris_info.work_mode.tx_mode) + iris_dsi_parse_dcs_cmds(np, &ctrl_pdata->on_cmds, + "qcom,mdss-dsi-on-command-to-video-panel", "qcom,mdss-dsi-on-command-to-video-panel-state"); + else + iris_dsi_parse_dcs_cmds(np, &ctrl_pdata->on_cmds, + "qcom,mdss-dsi-on-command-to-cmd-panel", "qcom,mdss-dsi-on-command-to-cmd-panel-state"); +} + +void iris_tx_switch_cmd_parse(struct device_node *np, struct iris_info_t *piris_info) +{ + struct iris_tx_switch_cmd *ptx_switch_cmd = &(piris_info->tx_switch_cmd); + + iris_dsi_parse_dcs_cmds(np, &(ptx_switch_cmd->mipitx_vid2cmd_cmds), + "qcom,video-to-cmd-mode-switch-commands", "qcom,video-to-cmd-mode-switch-commands-state"); + iris_dsi_parse_dcs_cmds(np, &(ptx_switch_cmd->mipitx_cmd2vid_cmds), + "qcom,cmd-to-video-mode-switch-commands", "qcom,cmd-to-video-mode-switch-commands-state"); + +} + +void iris_intf_switch_info_parse(struct device_node *np, struct iris_info_t *piris_info) +{ + struct iris_intf_switch_info *pswitch_info = &(piris_info->intf_switch_info); + + pswitch_info->rx_switch_enable = of_property_read_bool(np, + "qcom,dynamic-mode-switch-enabled"); + pswitch_info->rx_current_mode = piris_info->work_mode.rx_mode; + + pswitch_info->tx_switch_enable = of_property_read_bool(np, + "iris,mipitx-dynamic-mode-switch-enabled"); + pswitch_info->tx_current_mode = piris_info->work_mode.tx_mode; + +} + +void iris_analog_bypass_info_parse(struct device_node *np, struct iris_info_t *piris_info) +{ + struct iris_abypass_ctrl *pabypss_ctrl = &(piris_info->abypss_ctrl); + + pabypss_ctrl->analog_bypass_enable = of_property_read_bool(np, + "iris,analog-bypass-mode-enabled"); + + pabypss_ctrl->abypass_status = PASS_THROUGH_MODE; + pabypss_ctrl->abypass_debug = false; + pabypss_ctrl->abypass_to_pt_enable = 0; + pabypss_ctrl->pt_to_abypass_enable = 0; + pabypss_ctrl->abypss_switch_state = PASS_THROUGH_STATE; + pabypss_ctrl->base_time = 0; + pabypss_ctrl->frame_delay = 0; + +} + +void iris_info_structure_init(void) +{ + memset(&iris_info, 0, sizeof(iris_info)); + + iris_info.firmware_info.firmware_size = 0x40000; + iris_info.firmware_info.fw_dw_result = FAILED; +} + + +void iris_init_params_parse(struct device_node *np, struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + struct mdss_panel_info *panel_info = &(ctrl_pdata->panel_data.panel_info); + + if (panel_info->pdest == DISPLAY_1) { + iris_info_structure_init(); + g_ctrl = ctrl_pdata; + + iris_workmode_parse(np, panel_info, &iris_info); + iris_timing_parse(np, panel_info, &iris_info); + iris_dsc_info_parse(np, panel_info, &iris_info); + iris_setting_info_parse(np, panel_info, &iris_info); + iris_tx_switch_cmd_parse(np, &iris_info); + iris_intf_switch_info_parse(np, &iris_info); + iris_analog_bypass_info_parse(np, &iris_info); + } + if (iris_info.work_mode.rx_mode != iris_info.work_mode.tx_mode) + iris_on_cmds_parse(np, ctrl_pdata); +} + +void iris_cmd_reg_add(struct iris_grcp_cmd *pcmd, u32 addr, u32 val) +{ + *(u32 *)(pcmd->cmd + pcmd->cmd_len) = cpu_to_le32(addr); + *(u32 *)(pcmd->cmd + pcmd->cmd_len + 4) = cpu_to_le32(val); + pcmd->cmd_len += 8; +} + +void iris_sys_reg_config(struct iris_info_t *piris_info, struct iris_grcp_cmd *pcmd) +{ + u32 clkmux_ctrl = 0x42180102, clkdiv_ctrl = 0x08; + u8 escclk_src, escclk_div, escclk_div_en; + + escclk_src = piris_info->setting_info.tx_escclk_setting & 0x0f; + escclk_div = (piris_info->setting_info.tx_escclk_setting >> 4) & 0x0f; + escclk_div_en = (piris_info->setting_info.tx_escclk_setting >> 8) & 0x1; + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + CLKMUX_CTRL, clkmux_ctrl | (escclk_src << 11)); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + CLKDIV_CTRL, clkdiv_ctrl | (escclk_div << 3) | (escclk_div_en << 7)); + + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + PPLL_B_CTRL1, piris_info->setting_info.ppll1); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + PPLL_B_CTRL2, piris_info->setting_info.ppll2); + +// iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + 0x150, piris_info->setting_info.dpll0); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + DPLL_B_CTRL1, piris_info->setting_info.dpll1); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + DPLL_B_CTRL2, piris_info->setting_info.dpll2); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + PLL_CTRL, 0x1800a); + iris_cmd_reg_add(pcmd, IRIS_SYS_ADDR + PLL_CTRL, 0x18000); + +} + +void iris_mipirx_reg_config(struct iris_info_t *piris_info, struct iris_grcp_cmd *pcmd) +{ + struct iris_work_mode *pwork_mode = &(piris_info->work_mode); + struct iris_timing_info *pinput_timing = &(piris_info->input_timing); + struct iris_setting_info *psetting_info = &(piris_info->setting_info); + u32 dbi_handler_ctrl = 0, frame_col_addr = 0; + u32 rx_ch, mipirx_addr = IRIS_MIPI_RX_ADDR; + + for (rx_ch = 0; rx_ch < (pwork_mode->rx_ch + 1); rx_ch++) { +#ifdef MIPI_SWAP + mipirx_addr -= rx_ch * IRIS_MIPI_ADDR_OFFSET; +#else + mipirx_addr += rx_ch * IRIS_MIPI_ADDR_OFFSET; +#endif + iris_cmd_reg_add(pcmd, mipirx_addr + DEVICE_READY, 0x00000000); + /*reset for DFE*/ + iris_cmd_reg_add(pcmd, mipirx_addr + RESET_ENABLE_DFE, 0x00000000); + iris_cmd_reg_add(pcmd, mipirx_addr + RESET_ENABLE_DFE, 0x00000001); + + dbi_handler_ctrl = 0xf0000 + (pwork_mode->rx_ch << 23); + /* left side enable */ + if (pwork_mode->rx_ch && (0 == rx_ch)) + dbi_handler_ctrl += (1 << 24); + /*ext_mipi_rx_ctrl*/ + if (1 == rx_ch) + dbi_handler_ctrl += (1 << 22); + iris_cmd_reg_add(pcmd, mipirx_addr + DBI_HANDLER_CTRL, dbi_handler_ctrl); + + if (pwork_mode->rx_mode) { + frame_col_addr = (pwork_mode->rx_ch) ? (pinput_timing->hres * 2 - 1) : (pinput_timing->hres - 1); + iris_cmd_reg_add(pcmd, mipirx_addr + FRAME_COLUMN_ADDR, frame_col_addr << 16); + iris_cmd_reg_add(pcmd, mipirx_addr + ABNORMAL_COUNT_THRES, 0xffffffff); + } + iris_cmd_reg_add(pcmd, mipirx_addr + DSI_FUNCTIONAL_PROGRAMMING, psetting_info->mipirx_dsi_functional_program); + iris_cmd_reg_add(pcmd, mipirx_addr + EOT_ECC_CRC_DISABLE, psetting_info->mipirx_eot_ecc_crc_disable); + iris_cmd_reg_add(pcmd, mipirx_addr + DATA_LANE_TIMING_PARAMETER, psetting_info->mipirx_data_lane_timing_param); + iris_cmd_reg_add(pcmd, mipirx_addr + DPI_SYNC_COUNT, pinput_timing->hsw + (pinput_timing->vsw << 16)); + iris_cmd_reg_add(pcmd, mipirx_addr + DEVICE_READY, 0x00000001); + } +} + +void iris_mipitx_reg_config(struct iris_info_t *piris_info, struct iris_grcp_cmd *pcmd) +{ + struct iris_work_mode *pwork_mode = &(piris_info->work_mode); + struct iris_setting_info *psetting_info = &(piris_info->setting_info); + struct iris_timing_info *poutput_timing = &(piris_info->output_timing); + u32 tx_ch, mipitx_addr = IRIS_MIPI_TX_ADDR, dual_ch_ctrl, dsi_tx_ctrl = 0; + + for (tx_ch = 0; tx_ch < (pwork_mode->tx_ch + 1); tx_ch++) { +#ifdef MIPI_SWAP + mipitx_addr -= tx_ch * IRIS_MIPI_ADDR_OFFSET; +#else + mipitx_addr += tx_ch * IRIS_MIPI_ADDR_OFFSET; +#endif + + if (pwork_mode->tx_mode) + dsi_tx_ctrl = psetting_info->mipitx_dsi_tx_ctrl | (0x1 << 8); + else + dsi_tx_ctrl = psetting_info->mipitx_dsi_tx_ctrl & (~(0x1 << 8)); + iris_cmd_reg_add(pcmd, mipitx_addr + DSI_TX_CTRL, dsi_tx_ctrl & 0xfffffffe); + + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_TIMING_MARGIN, psetting_info->mipitx_dphy_timing_margin); +#ifdef FPGA_DEBUG + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_LP_TIMING_PARA, psetting_info->mipitx_lp_timing_para + 0x6600); +#else + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_LP_TIMING_PARA, psetting_info->mipitx_lp_timing_para); +#endif + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_DATA_LANE_TIMING_PARA, psetting_info->mipitx_data_lane_timing_param); + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_CLOCK_LANE_TIMING_PARA, psetting_info->mipitx_clk_lane_timing_param); + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_PLL_PARA, psetting_info->mipitx_dphy_pll_para); + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_TRIM_1, psetting_info->mipitx_dphy_trim_1); + iris_cmd_reg_add(pcmd, mipitx_addr + DPHY_CTRL, 1); + + if (pwork_mode->tx_ch) { + dual_ch_ctrl = pwork_mode->tx_ch + ((poutput_timing->hres * 2) << 16); + if (0 == tx_ch) + dual_ch_ctrl += 1 << 1; + iris_cmd_reg_add(pcmd, mipitx_addr + DUAL_CH_CTRL, dual_ch_ctrl); + } + iris_cmd_reg_add(pcmd, mipitx_addr + HS_TX_TIMER, psetting_info->mipitx_hs_tx_timer); + iris_cmd_reg_add(pcmd, mipitx_addr + BTA_LP_TIMER, psetting_info->mipitx_bta_lp_timer); + iris_cmd_reg_add(pcmd, mipitx_addr + INITIALIZATION_RESET_TIMER, psetting_info->mipitx_initialization_reset_timer); + + iris_cmd_reg_add(pcmd, mipitx_addr + TX_RESERVED_0, 4); + + iris_cmd_reg_add(pcmd, mipitx_addr + DSI_TX_CTRL, dsi_tx_ctrl); + } + +} + +void iris_init_cmd_setup(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + u32 cnt = 0, grcp_len = 0; + struct mdss_panel_info *panel_info = &(ctrl_pdata->panel_data.panel_info); + + if (DISPLAY_1 == panel_info->pdest) { + memset(init_cmd, 0, sizeof(init_cmd)); + for (cnt = 0; cnt < INIT_CMD_NUM; cnt++) { + memcpy(init_cmd[cnt].cmd, grcp_header, GRCP_HEADER); + init_cmd[cnt].cmd_len = GRCP_HEADER; + } + + iris_mipirx_reg_config(&iris_info, &init_cmd[0]); + iris_sys_reg_config(&iris_info, &init_cmd[0]); + + iris_mipitx_reg_config(&iris_info, &init_cmd[1]); + + for (cnt = 0; cnt < INIT_CMD_NUM; cnt++) { + grcp_len = (init_cmd[cnt].cmd_len - GRCP_HEADER) / 4; + *(u32 *)(init_cmd[cnt].cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(init_cmd[cnt].cmd + 14) = cpu_to_le16(grcp_len); + } + } +} + +void iris_mipirx_mode_set(struct mdss_dsi_ctrl_pdata *ctrl, int mode, int state) +{ + char mipirx_mode[1] = {0x3f}; + struct dsi_cmd_desc iris_mipirx_mode_cmds[] = { + {{ DTYPE_GEN_WRITE1, 1, 0, 0, CMD_PROC, sizeof(mipirx_mode)}, mipirx_mode}, + }; + struct dsi_panel_cmds panel_cmds; + + switch (mode) { + case MCU_VIDEO: + mipirx_mode[0] = 0x3f; + break; + case MCU_CMD: + mipirx_mode[0] = 0x1f; + break; + case PWIL_VIDEO: + mipirx_mode[0] = 0xbf; + break; + case PWIL_CMD: + mipirx_mode[0] = 0x7f; + break; + case BYPASS_VIDEO: + mipirx_mode[0] = 0xff; + break; + case BYPASS_CMD: + mipirx_mode[0] = 0xdf; + break; + default: + break; + } + pr_info("iris: set mipirx mode: %d\n", mode); + + panel_cmds.cmds = iris_mipirx_mode_cmds; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_mipirx_mode_cmds); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(mipirx_mode ,sizeof(mipirx_mode)); +} + +void iris_init_cmd_send(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + struct dsi_cmd_desc iris_init_info_cmds[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, CMD_PKT_SIZE}, init_cmd[0].cmd}, + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, CMD_PKT_SIZE}, init_cmd[1].cmd}, + }; + struct dsi_panel_cmds panel_cmds; + + iris_init_info_cmds[0].dchdr.dlen = init_cmd[0].cmd_len; + iris_init_info_cmds[1].dchdr.dlen = init_cmd[1].cmd_len; + + pr_info("iris: send init cmd\n"); + + panel_cmds.cmds = iris_init_info_cmds; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_init_info_cmds); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(init_cmd[0].cmd, init_cmd[0].cmd_len); + DUMP_PACKET(init_cmd[1].cmd, init_cmd[1].cmd_len); +} +#if 0 +void iris_timing_info_send(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + char iris_workmode[] = { + 0x80, 0x87, 0x0, 0x3, + PWIL_U32(0x0), + }; + char iris_timing[] = { + 0x80, 0x87, 0x0, 0x0, + PWIL_U32(0x01e00010), + PWIL_U32(0x00160010), + PWIL_U32(0x0320000a), + PWIL_U32(0x00080008), + PWIL_U32(0x3c1f), + PWIL_U32(0x01e00014), + PWIL_U32(0x00160010), + PWIL_U32(0x0320000a), + PWIL_U32(0x00080008), + PWIL_U32(0x3c1f), + PWIL_U32(0x00100008), + PWIL_U32(0x80), + PWIL_U32(0x00100008), + PWIL_U32(0x80) + }; + struct dsi_cmd_desc iris_timing_info_cmd[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(iris_workmode)}, iris_workmode}, + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(iris_timing)}, iris_timing} + }; + struct iris_timing_info *pinput_timing = &(iris_info.input_timing); + struct iris_timing_info *poutput_timing = &(iris_info.output_timing); + struct iris_dsc_info *pinput_dsc = &(iris_info.input_dsc); + struct iris_dsc_info *poutput_dsc = &(iris_info.output_dsc); + struct dsi_panel_cmds panel_cmds; + + memcpy(iris_workmode + 4, &(iris_info.work_mode), 4); + + *(u32 *)(iris_timing + 4) = cpu_to_le32((pinput_timing->hres << 16) + pinput_timing->hfp); + *(u32 *)(iris_timing + 8) = cpu_to_le32((pinput_timing->hsw << 16) + pinput_timing->hbp); + *(u32 *)(iris_timing + 12) = cpu_to_le32((pinput_timing->vres << 16) + pinput_timing->vfp); + *(u32 *)(iris_timing + 16) = cpu_to_le32((pinput_timing->vsw << 16) + pinput_timing->vbp); + *(u32 *)(iris_timing + 20) = cpu_to_le32((pinput_timing->fps << 8) + 0x1f); + + *(u32 *)(iris_timing + 24) = cpu_to_le32((poutput_timing->hres << 16) + poutput_timing->hfp); + *(u32 *)(iris_timing + 28) = cpu_to_le32((poutput_timing->hsw << 16) + poutput_timing->hbp); + *(u32 *)(iris_timing + 32) = cpu_to_le32((poutput_timing->vres << 16) + poutput_timing->vfp); + *(u32 *)(iris_timing + 36) = cpu_to_le32((poutput_timing->vsw << 16) + poutput_timing->vbp); + *(u32 *)(iris_timing + 40) = cpu_to_le32((poutput_timing->fps << 8) + 0x1f); + + *(u32 *)(iris_timing + 44) = cpu_to_le32((pinput_dsc->slice_height << 16) + pinput_dsc->slice_number); + *(u32 *)(iris_timing + 48) = cpu_to_le32(pinput_dsc->bpp); + + *(u32 *)(iris_timing + 52) = cpu_to_le32((poutput_dsc->slice_height << 16) + poutput_dsc->slice_number); + *(u32 *)(iris_timing + 56) = cpu_to_le32(poutput_dsc->bpp); + + pr_info("iris: send timing info\n"); + + panel_cmds.cmds = iris_timing_info_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_timing_info_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(iris_workmode, sizeof(iris_workmode)); + DUMP_PACKET(iris_timing, sizeof(iris_timing)); +} + +void iris_timing_info_grcp_send(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + static char iris_timing[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000011), + 0x03, + 0x00, + PWIL_U16(0x0f), + PWIL_U32(IRIS_GRCP_BUFFER_ADDR), + PWIL_U32(0x00800000), /*work mode*/ + PWIL_U32(0x01e00014), /*Input's HRES+HFP*/ + PWIL_U32(0x00160010), /*Input's HSW+HBP*/ + PWIL_U32(0x0320000a), /*Input's VRES+VFP*/ + PWIL_U32(0x00080008), /*Input's VSW+VBP*/ + PWIL_U32(0x1f), /*Input's DenPol, HsPol, VsPol etc.*/ + PWIL_U32(0x01e00014), /*Output's HRES+HFP*/ + PWIL_U32(0x00160010), /*Output's HSW+HBP*/ + PWIL_U32(0x0320000a), /*OutPut's VRES+VFP*/ + PWIL_U32(0x00080008), /*Output's VSW+VBP*/ + PWIL_U32(0x1f), /*Output's DenPol, HsPol, VsPol etc.*/ + PWIL_U32(0x00100008), /*Input's SliceHeight+SliceNumber*/ + PWIL_U32(0x80), /*wDscBPP*/ + PWIL_U32(0x00100008), /*Output's SliceHeight+SliceNumber*/ + PWIL_U32(0x80), /*wDscBPP*/ + }; + + struct dsi_cmd_desc iris_timing_info_cmd[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(iris_timing)}, iris_timing} + }; + struct iris_timing_info *pinput_timing = &(iris_info.input_timing); + struct iris_timing_info *poutput_timing = &(iris_info.output_timing); + struct iris_dsc_info *pinput_dsc = &(iris_info.input_dsc); + struct iris_dsc_info *poutput_dsc = &(iris_info.output_dsc); + struct dsi_panel_cmds panel_cmds; + + memcpy(iris_timing + 20, &(iris_info.work_mode), 4); + + *(u32 *)(iris_timing + 24) = cpu_to_le32((pinput_timing->hres << 16) + pinput_timing->hfp); + *(u32 *)(iris_timing + 28) = cpu_to_le32((pinput_timing->hsw << 16) + pinput_timing->hbp); + *(u32 *)(iris_timing + 32) = cpu_to_le32((pinput_timing->vres << 16) + pinput_timing->vfp); + *(u32 *)(iris_timing + 36) = cpu_to_le32((pinput_timing->vsw << 16) + pinput_timing->vbp); + *(u32 *)(iris_timing + 40) = cpu_to_le32((pinput_timing->fps << 8) + 0x1f); + + *(u32 *)(iris_timing + 44) = cpu_to_le32((poutput_timing->hres << 16) + poutput_timing->hfp); + *(u32 *)(iris_timing + 48) = cpu_to_le32((poutput_timing->hsw << 16) + poutput_timing->hbp); + *(u32 *)(iris_timing + 52) = cpu_to_le32((poutput_timing->vres << 16) + poutput_timing->vfp); + *(u32 *)(iris_timing + 56) = cpu_to_le32((poutput_timing->vsw << 16) + poutput_timing->vbp); + *(u32 *)(iris_timing + 60) = cpu_to_le32((poutput_timing->fps << 8) + 0x1f); + + *(u32 *)(iris_timing + 64) = cpu_to_le32((pinput_dsc->slice_height << 16) + pinput_dsc->slice_number); + *(u32 *)(iris_timing + 68) = cpu_to_le32(pinput_dsc->bpp); + + *(u32 *)(iris_timing + 72) = cpu_to_le32((poutput_dsc->slice_height << 16) + poutput_dsc->slice_number); + *(u32 *)(iris_timing + 76) = cpu_to_le32(poutput_dsc->bpp); + + pr_info("iris: send timing info\n"); + + panel_cmds.cmds = iris_timing_info_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_timing_info_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); +} + +void iris_grcp_buffer_init(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + static char grcp_buf_init[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x0000000a), + 0x03, + 0x00, + PWIL_U16(0x08), + PWIL_U32(IRIS_GRCP_CTRL_ADDR + 0x12fe0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0), + PWIL_U32(0x0) + }; + struct dsi_cmd_desc iris_grcp_buf_init_cmd[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(grcp_buf_init)}, grcp_buf_init} + }; + struct dsi_panel_cmds panel_cmds; + + + pr_info("iris: init GRCP buffer\n"); + + panel_cmds.cmds = iris_grcp_buf_init_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_grcp_buf_init_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(grcp_buf_init, sizeof(grcp_buf_init)); +} + +void iris_ctrl_cmd_send(struct mdss_dsi_ctrl_pdata *ctrl, u8 cmd, int state) +{ + char romcode_ctrl[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000005), + 0x00, + 0x00, + PWIL_U16(0x04), + PWIL_U32(IRIS_MODE_ADDR), /*proxy_MB1*/ + PWIL_U32(0x00000000), + PWIL_U32(IRIS_PROXY_MB7_ADDR), /*proxy_MB7*/ + PWIL_U32(0x00040000), + }; + struct dsi_cmd_desc iris_romcode_ctrl_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(romcode_ctrl)}, romcode_ctrl}, + }; + struct dsi_panel_cmds panel_cmds; + + if ((cmd | CONFIG_DATAPATH) || (cmd | ENABLE_DPORT) || (cmd | REMAP)) + iris_romcode_ctrl_cmd[0].dchdr.wait = INIT_WAIT; + + romcode_ctrl[20] = cmd; + *(u32 *)(romcode_ctrl + 28) = cpu_to_le32(iris_info.firmware_info.firmware_size); + + pr_info("iris: send romcode ctrl cmd: %x\n", cmd); + + panel_cmds.cmds = iris_romcode_ctrl_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_romcode_ctrl_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(romcode_ctrl, sizeof(romcode_ctrl)); +} + +void iris_dtg_set(struct mdss_dsi_ctrl_pdata *ctrl, int state) +{ + u32 grcp_len = 0; + struct mdss_panel_info *panel_info = &(ctrl->panel_data.panel_info); + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + struct dsi_cmd_desc iris_dtg_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, CMD_PKT_SIZE}, grcp_cmd.cmd}, + }; + struct dsi_panel_cmds panel_cmds; + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + if (DISPLAY_1 != panel_info->pdest) { + return; + } + + memset(&grcp_cmd, 0, sizeof(grcp_cmd)); + memcpy(grcp_cmd.cmd, grcp_header, GRCP_HEADER); + grcp_cmd.cmd_len = GRCP_HEADER; + + iris_dtg_para_set((int)pwork_mode->rx_mode, (int)pwork_mode->tx_mode); + + + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + DTG_DELAY, iris_cfg->dtg_setting.dtg_delay); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL, iris_cfg->dtg_setting.te_ctrl); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL_1, iris_cfg->dtg_setting.te_ctrl_1); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL_2, iris_cfg->dtg_setting.te_ctrl_2); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL_3, iris_cfg->dtg_setting.te_ctrl_3); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL_4, iris_cfg->dtg_setting.te_ctrl_4); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_CTRL_5, iris_cfg->dtg_setting.te_ctrl_5); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + DTG_CTRL_1,iris_cfg->dtg_setting.dtg_ctrl_1); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + DTG_CTRL,iris_cfg->dtg_setting.dtg_ctrl); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + EVS_DLY,iris_cfg->dtg_setting.evs_dly); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + EVS_NEW_DLY,iris_cfg->dtg_setting.evs_new_dly); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + DVS_CTRL, iris_cfg->dtg_setting.dvs_ctrl); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_DLY, iris_cfg->dtg_setting.te_dly); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + TE_DLY_1, iris_cfg->dtg_setting.te_dly_1); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + VFP_CTRL_0, iris_cfg->dtg_setting.vfp_ctrl_0); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + VFP_CTRL_1, iris_cfg->dtg_setting.vfp_ctrl_1); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + DTG_RESERVE, 0x1000000b); + iris_cmd_reg_add(&grcp_cmd, IRIS_DTG_ADDR + REGSEL, 1); + + grcp_len = (grcp_cmd.cmd_len - GRCP_HEADER) / 4; + *(u32 *)(grcp_cmd.cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(grcp_cmd.cmd + 14) = cpu_to_le16(grcp_len); + + iris_dtg_cmd[0].dchdr.dlen = grcp_cmd.cmd_len; + + pr_info("iris: set dtg \n"); + + panel_cmds.cmds = iris_dtg_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_dtg_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(grcp_cmd.cmd, grcp_cmd.cmd_len); +} + +void iris_pwil_mode_set(struct mdss_dsi_ctrl_pdata *ctrl, u8 mode, int state) +{ + char pwil_mode[2] = {0x00, 0x00}; + struct dsi_cmd_desc iris_pwil_mode_cmd[] = { + {{DTYPE_GEN_WRITE2, 1, 0, 0, CMD_PROC, sizeof(pwil_mode)}, pwil_mode}, + }; + struct dsi_panel_cmds panel_cmds; + + if (PT_MODE == mode) { + pwil_mode[0] = 0x0; + pwil_mode[1] = 0x1; + } else if (RFB_MODE == mode) { + pwil_mode[0] = 0xc; + pwil_mode[1] = 0x1; + } else if (BIN_MODE == mode) { + pwil_mode[0] = 0xc; + pwil_mode[1] = 0x20; + } + + pr_info("iris: set pwil mode: %x, %x\n", pwil_mode[0], pwil_mode[1]); + + panel_cmds.cmds = iris_pwil_mode_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_pwil_mode_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(pwil_mode, sizeof(pwil_mode)); +} + +void iris_mipi_mem_addr_set(struct mdss_dsi_ctrl_pdata *ctrl, u16 column, u16 page, int state) +{ + char mem_addr[2] = {0x36, 0x0}; + char pixel_format[2] = {0x3a, 0x77}; + char col_addr[5] = {0x2a, 0x00, 0x00, 0x03, 0xff}; + char page_addr[5] = {0x2b, 0x00, 0x00, 0x03, 0xff}; + struct dsi_cmd_desc iris_mem_addr_cmd[] = { + {{DTYPE_DCS_WRITE1, 0, 0, 0, 0, sizeof(mem_addr)}, mem_addr}, + {{DTYPE_DCS_WRITE1, 0, 0, 0, 0, sizeof(pixel_format)}, pixel_format}, + {{DTYPE_DCS_LWRITE, 0, 0, 0, 0, sizeof(col_addr)}, col_addr}, + {{DTYPE_DCS_LWRITE, 1, 0, 0, 0, sizeof(page_addr)}, page_addr}, + }; + struct dsi_panel_cmds panel_cmds; + + col_addr[3] = (column >> 8) & 0xff; + col_addr[4] = column & 0xff; + page_addr[3] = (page >> 8) & 0xff; + page_addr[4] = page & 0xff; + + pr_debug("iris: set mipi mem addr: %x, %x\n", column, page); + + panel_cmds.cmds = iris_mem_addr_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_mem_addr_cmd); + panel_cmds.link_state = state; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + DUMP_PACKET(mem_addr, sizeof(mem_addr)); + DUMP_PACKET(pixel_format, sizeof(pixel_format)); + DUMP_PACKET(col_addr, sizeof(col_addr)); + DUMP_PACKET(page_addr, sizeof(page_addr)); +} + +void iris_firmware_download_prepare(struct mdss_dsi_ctrl_pdata *ctrl, size_t size) +{ +#define TIME_INTERVAL 20 /*ms*/ + + char fw_download_config[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x000000013), + 0x00, + 0x00, + PWIL_U16(0x0012), + PWIL_U32(IRIS_PWIL_ADDR + 0x0004), /*PWIL ctrl1 confirm transfer mode and cmd mode, single channel.*/ + PWIL_U32(0x00004144), + PWIL_U32(IRIS_PWIL_ADDR + 0x0218), /*CAPEN*/ + PWIL_U32(0xc0000003), + PWIL_U32(IRIS_PWIL_ADDR + 0x1140), /*channel order*/ + PWIL_U32(0xc6120010), + PWIL_U32(IRIS_PWIL_ADDR + 0x1144), /*pixelformat*/ + PWIL_U32(0x888), + PWIL_U32(IRIS_PWIL_ADDR + 0x1158), /*mem addr*/ + PWIL_U32(0x00000000), + PWIL_U32(IRIS_PWIL_ADDR + 0x10000), /*update setting. using SW update mode*/ + PWIL_U32(0x00000100), + PWIL_U32(IRIS_PWIL_ADDR + 0x1fff0), /*clear down load int*/ + PWIL_U32(0x00008000), + PWIL_U32(IRIS_MIPI_RX_ADDR + 0xc), /*mipi_rx setting DBI_bus*/ + PWIL_U32(0x000f0000), + PWIL_U32(IRIS_MIPI_RX_ADDR + 0x001c), /*mipi_rx time out threshold*/ + PWIL_U32(0xffffffff) + }; + u32 threshold = 0, fw_hres, fw_vres; + struct dsi_panel_cmds panel_cmds; + struct dsi_cmd_desc fw_download_config_cmd[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(fw_download_config)}, fw_download_config} + }; + + threshold = ctrl->pclk_rate / 1000; + threshold *= TIME_INTERVAL; + *(u32 *)(fw_download_config + 84) = cpu_to_le32(threshold); + + /*firmware download need mipirx work on single cmd mode, pwil work on binarary mode.*/ + panel_cmds.cmds = fw_download_config_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(fw_download_config_cmd); + panel_cmds.link_state = DSI_HS_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + iris_pwil_mode_set(ctrl, BIN_MODE, DSI_HS_MODE); + + fw_hres = FW_COL_CNT - 1; + fw_vres = (size + FW_COL_CNT * 3 - 1) / (FW_COL_CNT * 3) - 1; + iris_mipi_mem_addr_set(ctrl, fw_hres, fw_vres, DSI_HS_MODE); + +} + +void iris_firmware_download_restore(struct mdss_dsi_ctrl_pdata *ctrl) +{ + char fw_download_restore[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000007), + 0x00, + 0x00, + PWIL_U16(0x06), + PWIL_U32(IRIS_PWIL_ADDR + 0x0004), + PWIL_U32(0x00004140), + PWIL_U32(IRIS_MIPI_RX_ADDR + 0xc), + PWIL_U32(0x000f0000), + PWIL_U32(IRIS_MIPI_RX_ADDR + 0x001c), + PWIL_U32(0xffffffff) + }; + u32 col_addr = 0, page_addr = 0; + struct dsi_panel_cmds panel_cmds; + struct dsi_cmd_desc fw_download_restore_cmd[] = { + {{DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(fw_download_restore)}, fw_download_restore} + }; + + if (MIPI_CMD_MODE == iris_info.work_mode.rx_mode) + fw_download_restore[20] += (2 << 1); + + if (1 == iris_info.work_mode.rx_ch) { + fw_download_restore[20] += 1; + fw_download_restore[30] = 0x8f; + } + + if (1 == iris_info.work_mode.rx_ch) + col_addr = iris_info.input_timing.hres * 2 - 1; + else + col_addr = iris_info.input_timing.hres - 1; + + page_addr = iris_info.input_timing.vres - 1; + + panel_cmds.cmds = fw_download_restore_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(fw_download_restore_cmd); + panel_cmds.link_state = DSI_HS_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + iris_pwil_mode_set(ctrl, RFB_MODE, DSI_HS_MODE); + + if (MIPI_CMD_MODE == iris_info.work_mode.rx_mode) + iris_mipi_mem_addr_set(ctrl, col_addr, page_addr, DSI_HS_MODE); +} + + +int iris_firmware_data_send(struct mdss_dsi_ctrl_pdata *ctrl, const u8 *fw_data, size_t fw_size) +{ + u8 *buf = NULL; + u32 pkt_size = FW_COL_CNT * 3, cmd_len = 0, cmd_cnt = 0, buf_indx = 0, cmd_indx = 0; + static struct dsi_cmd_desc fw_send_cmd[FW_DW_CMD_CNT]; + struct dsi_panel_cmds panel_cmds; + + buf = kzalloc(DSI_DMA_TX_BUF_SIZE, GFP_KERNEL); + if (!buf) { + pr_err("%s: failed to alloc mem, size = %d\n", __func__, DSI_DMA_TX_BUF_SIZE); + return FAILED; + } + memset(buf, 0, DSI_DMA_TX_BUF_SIZE); + memset(fw_send_cmd, 0, sizeof(fw_send_cmd)); + + while (fw_size) { + if (fw_size >= pkt_size) + cmd_len = pkt_size; + else + cmd_len = fw_size; + + if (0 == cmd_cnt) + buf[0] = DCS_WRITE_MEM_START; + else + buf[buf_indx] = DCS_WRITE_MEM_CONTINUE; + + memcpy(buf + buf_indx + 1, fw_data, cmd_len); + + cmd_indx = cmd_cnt % FW_DW_CMD_CNT; + fw_send_cmd[cmd_indx].dchdr.last = 0; + fw_send_cmd[cmd_indx].dchdr.dtype = 0x39; + fw_send_cmd[cmd_indx].dchdr.dlen = pkt_size + 1; + fw_send_cmd[cmd_indx].payload = buf + buf_indx; + + fw_size -= cmd_len; + fw_data += cmd_len; + cmd_cnt++; + buf_indx += cmd_len + 1; + + if (((FW_DW_CMD_CNT - 1) == cmd_indx) || (0 == fw_size)) { + fw_send_cmd[cmd_indx].dchdr.last = 1; + panel_cmds.cmds = fw_send_cmd; + panel_cmds.cmd_cnt = cmd_indx + 1; + panel_cmds.link_state = DSI_HS_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + buf_indx = 0; + } + } + + kfree(buf); + + return SUCCESS; +} + +static void iris_mipirx_status_cb(int len) +{ + if (len != 2) { + pr_err("%s: not short read responese, return len [%02x] != 2\n", __func__, len); + return; + } + + iris_mipirx_status = (iris_read_cmd_buf[0] & 0xFF) | ((iris_read_cmd_buf[1] & 0x0f) << 8); + pr_info("mipi_rx result [%04x]\n", iris_mipirx_status); +} + +static u16 iris_fw_download_result_read(struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct dcs_cmd_req cmdreq; + char mipirx_status[1] = {0xaf}; + struct dsi_cmd_desc mipirx_status_cmd = { + {DTYPE_DCS_READ, 1, 0, 1, 0, sizeof(mipirx_status)}, mipirx_status}; + + memset(iris_read_cmd_buf, 0, sizeof(iris_read_cmd_buf)); + iris_mipirx_status = 0; + + cmdreq.cmds = &mipirx_status_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT | CMD_REQ_LP_MODE; + cmdreq.rlen = 2; + cmdreq.rbuf = iris_read_cmd_buf; + cmdreq.cb = iris_mipirx_status_cb; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + + return (iris_mipirx_status & 0x0f00); +} + +int iris_fw_download_result_check(struct mdss_dsi_ctrl_pdata *ctrl) +{ + u16 cnt = 0, result = 0; + + do { + result = iris_fw_download_result_read(ctrl); + if (0x0100 == result) + break; + + msleep(2); + cnt++; + } while ((result != 0x0100) && cnt < 5); + + /*read failed*/ + if (5 == cnt) { + pr_err("firmware download failed\n"); + return FAILED; + } else + pr_info("firmware download success\n"); + + + return SUCCESS; +} + +int iris2p_firmware_download(struct mdss_dsi_ctrl_pdata *ctrl, struct msm_fb_data_type *mfd, const char *name) +{ + const struct firmware *fw = NULL; + int ret = 0, result = SUCCESS; + + iris_info.firmware_info.firmware_size = 0; + if (name) { + /* Firmware file must be in /system/etc/firmware/ */ + ret = request_firmware(&fw, name, mfd->fbi->dev); + if (ret) { + pr_err("%s: failed to request firmware: %s, ret = %d\n", + __func__, name, ret); + result = FAILED; + } else { + pr_info("%s: request firmware: name = %s, size = %zu bytes\n", + __func__, name, fw->size); + + iris_info.firmware_info.firmware_size = fw->size; + iris_firmware_download_prepare(ctrl, fw->size); + iris_firmware_data_send(ctrl, fw->data, fw->size); +#ifdef READ_CMD_ENABLE + result = iris_fw_download_result_check(ctrl); +#else + msleep(100); +#endif + iris_firmware_download_restore(ctrl); + release_firmware(fw); + } + } else { + pr_err("%s: firmware is null\n", __func__); + result = FAILED; + } + return result; +} +#endif +void iris_dport_disable(struct mdss_dsi_ctrl_pdata *ctrl) +{ + char dport_disable[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000003), + 0x00, + 0x00, + PWIL_U16(0x02), + PWIL_U32(IRIS_DPORT_ADDR + 0x04), /*dport_ctrl1*/ + PWIL_U32(0x21008800), + }; + struct dsi_cmd_desc iris_dport_disable_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, CMD_PROC, sizeof(dport_disable)}, dport_disable}, + }; + struct dsi_panel_cmds panel_cmds; + + pr_info("iris: send dport disable cmd\n"); + + panel_cmds.cmds = iris_dport_disable_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_dport_disable_cmd); + panel_cmds.link_state = DSI_HS_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + +} +#define EFUSE_REWRITE + + +#ifdef EFUSE_REWRITE +static void iris_sys_efuse_rewrite(struct mdss_dsi_ctrl_pdata *ctrl) +{ + u32 grcp_len = 0; + struct dsi_cmd_desc iris_grcp_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, 0, CMD_PKT_SIZE}, grcp_cmd.cmd}, + }; + struct dsi_panel_cmds panel_cmds; + + memset(&grcp_cmd, 0, sizeof(grcp_cmd)); + memcpy(grcp_cmd.cmd, grcp_header, GRCP_HEADER); + grcp_cmd.cmd_len = GRCP_HEADER; + + iris_cmd_reg_add(&grcp_cmd, IRIS_SYS_ADDR + DFT_EFUSE_CTRL, 0x80000100); + iris_cmd_reg_add(&grcp_cmd, IRIS_SYS_ADDR + DFT_EFUSE_CTRL_1, 0x0000200d); + + grcp_len = (grcp_cmd.cmd_len - GRCP_HEADER) / 4; + *(u32 *)(grcp_cmd.cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(grcp_cmd.cmd + 14) = cpu_to_le16(grcp_len); + + iris_grcp_cmd[0].dchdr.dlen = grcp_cmd.cmd_len; + + pr_info("iris efuse rewrite\n"); + + panel_cmds.cmds = iris_grcp_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_grcp_cmd); + panel_cmds.link_state = DSI_LP_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + DUMP_PACKET(grcp_cmd.cmd, grcp_cmd.cmd_len); + +} +#endif +extern int iris_one_wired_cmd_init(struct mdss_dsi_ctrl_pdata *ctrl); +extern void iris_one_wired_cmd_send(struct mdss_dsi_ctrl_pdata *ctrl, int cmd); +void iris_low_power_mode_enter(struct mdss_dsi_ctrl_pdata *ctrl) +{ + u32 grcp_len = 0; + struct dsi_cmd_desc iris_grcp_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, 0, CMD_PKT_SIZE}, grcp_cmd.cmd}, + }; + struct dsi_panel_cmds panel_cmds; + + memset(&grcp_cmd, 0, sizeof(grcp_cmd)); + memcpy(grcp_cmd.cmd, grcp_header, GRCP_HEADER); + grcp_cmd.cmd_len = GRCP_HEADER; + + + /* power down edram1~8 */ + iris_cmd_reg_add(&grcp_cmd, 0xf104000c, 0xfffffffe); + + /*disable DPHY*/ + iris_cmd_reg_add(&grcp_cmd, 0xf0180000, 0x0a00c138); + iris_cmd_reg_add(&grcp_cmd, 0xf0180004, 0x00000000); + + /* set clock source to XCLK */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000210, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000214, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000218, 0x00000f0b); + iris_cmd_reg_add(&grcp_cmd, 0xf000021c, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000228, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000000); + + /* power down PLL */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000140, 0x00000003); + iris_cmd_reg_add(&grcp_cmd, 0xf0000150, 0x00002003); + iris_cmd_reg_add(&grcp_cmd, 0xf0000160, 0x00000003); + + /* gate off clock */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000000, 0x3fc87fff); + iris_cmd_reg_add(&grcp_cmd, 0xf0000004, 0x00007f8e); + + + grcp_len = (grcp_cmd.cmd_len - GRCP_HEADER) / 4; + *(u32 *)(grcp_cmd.cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(grcp_cmd.cmd + 14) = cpu_to_le16(grcp_len); + + iris_grcp_cmd[0].dchdr.dlen = grcp_cmd.cmd_len; + + pr_info("iris low power mode\n"); + + panel_cmds.cmds = iris_grcp_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_grcp_cmd); + panel_cmds.link_state = DSI_LP_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + +} +void iris_init(struct mdss_dsi_ctrl_pdata *ctrl) +{ + //u32 column, page; + + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + +#ifdef READ_CMD_ENABLE + iris_proxy_check_reset(ctrl); + //iris_power_mode_check(ctrl, 0x01, DSI_LP_MODE); +#endif +#ifdef EFUSE_REWRITE + iris_mipirx_mode_set(ctrl, PWIL_CMD, DSI_LP_MODE); + iris_sys_efuse_rewrite(ctrl); + iris_power_mode_check(ctrl, 0x01, DSI_LP_MODE); +#endif + + iris_mipirx_mode_set(ctrl, PWIL_CMD, DSI_LP_MODE); + /* init sys/mipirx/mipitx */ + iris_init_cmd_send(ctrl, DSI_LP_MODE); + + iris_one_wired_cmd_init(ctrl); + iris_one_wired_cmd_send(ctrl, BYPASS_MODE_CHANGE); + msleep(100); + iris_low_power_mode_enter(ctrl); + return; +#if 0 + /* send work mode and timing info */ +#ifdef NEW_WORKFLOW + iris_timing_info_grcp_send(ctrl, DSI_LP_MODE); +#else + iris_mipirx_mode_set(ctrl, MCU_CMD, DSI_LP_MODE); + iris_timing_info_send(ctrl, DSI_LP_MODE); + iris_mipirx_mode_set(ctrl, PWIL_CMD, DSI_LP_MODE); +#endif + iris_ctrl_cmd_send(ctrl, CONFIG_DATAPATH, DSI_LP_MODE); + + /*init grcp buffer*/ + iris_grcp_buffer_init(ctrl, DSI_HS_MODE); + /* firmware download */ + iris_info.firmware_info.fw_dw_result = + iris2p_firmware_download(ctrl, gp_mfd, IRIS_FIRMWARE_NAME); + if (SUCCESS == iris_info.firmware_info.fw_dw_result) + iris_ctrl_cmd_send(ctrl, ITCM_COPY, DSI_HS_MODE); + + /* bypass panel on command */ + iris_mipirx_mode_set(ctrl, BYPASS_CMD, DSI_HS_MODE); + + /* set mipi_tx's mem addr */ + if (iris_info.work_mode.tx_mode) { + column = iris_info.work_mode.tx_ch ? (iris_info.output_timing.hres * 2 - 1) : (iris_info.output_timing.hres - 1); + page = iris_info.output_timing.vres - 1; + iris_mipi_mem_addr_set(ctrl, column, page, DSI_LP_MODE); + } +#endif +} + +void iris_lightup(struct mdss_dsi_ctrl_pdata *ctrl) +{ + //u32 column, page, cmd = ENABLE_DPORT; + + if (iris_info.work_mode.rx_ch) { + if (ctrl->ndx == DSI_CTRL_LEFT) + return; + else + ctrl = g_ctrl; + } + return; +#if 0 + if (SUCCESS == iris_info.firmware_info.fw_dw_result) + cmd += REMAP; + + if (MIPI_VIDEO_MODE == iris_info.work_mode.rx_mode) { + iris_mipirx_mode_set(ctrl, PWIL_VIDEO, DSI_HS_MODE); + iris_dtg_set(ctrl, DSI_HS_MODE); + iris_ctrl_cmd_send(ctrl, cmd, DSI_HS_MODE); + iris_pwil_mode_set(ctrl, RFB_MODE, DSI_HS_MODE); + } else { + iris_mipirx_mode_set(ctrl, PWIL_CMD, DSI_HS_MODE); + iris_dtg_set(ctrl, DSI_HS_MODE); + iris_ctrl_cmd_send(ctrl, cmd, DSI_HS_MODE); + iris_pwil_mode_set(ctrl, RFB_MODE, DSI_HS_MODE); + + column = iris_info.work_mode.rx_ch ? (iris_info.input_timing.hres * 2 - 1) : (iris_info.input_timing.hres - 1); + page = iris_info.input_timing.vres - 1; + iris_mipi_mem_addr_set(ctrl, column, page, DSI_HS_MODE); + } + + if (SUCCESS == iris_info.firmware_info.fw_dw_result) + { + iris_update_configure(); + gp_mfd->iris_conf.ready = true; + } + iris_info.low_power = 0; +#endif +} + +void iris_lightoff(struct mdss_dsi_ctrl_pdata *ctrl) +{ + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + + //gp_mfd->iris_conf.ready = false; + + //iris_mode_switch_reset(ctrl); + + /*disable dport*/ + iris_dport_disable(ctrl); + + /* bypass panel off command */ + iris_mipirx_mode_set(ctrl, BYPASS_CMD, DSI_HS_MODE); + +} + +void iris_panel_cmd_passthrough(struct mdss_dsi_ctrl_pdata *ctrl, struct dcs_cmd_req *cmdreq) +{ + u32 grcp_len = 0, cnt; + union iris_mipi_tx_cmd_header header; + union iris_mipi_tx_cmd_payload payload; + struct dsi_cmd_desc iris_grcp_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, 0, CMD_PKT_SIZE}, grcp_cmd.cmd}, + }; + struct dsi_panel_cmds panel_cmds; + struct dsi_cmd_desc *dsi_cmds = cmdreq->cmds; + + memset(&grcp_cmd, 0, sizeof(grcp_cmd)); + memcpy(grcp_cmd.cmd, grcp_header, GRCP_HEADER); + grcp_cmd.cmd_len = GRCP_HEADER; + + memset(&header, 0, sizeof(header)); + memset(&payload, 0, sizeof(payload)); + header.stHdr.dtype = dsi_cmds->dchdr.dtype; + + switch (dsi_cmds->dchdr.dtype) { + case DTYPE_DCS_WRITE: + case DTYPE_DCS_WRITE1: + case DTYPE_GEN_WRITE1: + case DTYPE_GEN_WRITE2: + //short write + header.stHdr.ecc = 0x1; + header.stHdr.len[0] = dsi_cmds->payload[0]; + if (dsi_cmds->dchdr.dlen == 2) + header.stHdr.len[1] = dsi_cmds->payload[1]; + pr_debug("%s, line%d, header=0x%4x\n", __func__, __LINE__, header.hdr32); + iris_cmd_reg_add(&grcp_cmd, IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, header.hdr32); + break; + case DTYPE_GEN_LWRITE: + //long write + header.stHdr.ecc = 0x5; + header.stHdr.len[0] = dsi_cmds->dchdr.dlen & 0xff; + header.stHdr.len[1] = (dsi_cmds->dchdr.dlen >> 8) & 0xff; + pr_debug("%s, line%d, header=0x%x\n", __func__, __LINE__, header.hdr32); + iris_cmd_reg_add(&grcp_cmd, IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, header.hdr32); + for (cnt = 0; cnt < dsi_cmds->dchdr.dlen; cnt = cnt+4) { + memcpy(payload.p, dsi_cmds->payload + cnt, 4); + pr_debug("payload=0x%x\n", payload.pld32); + iris_cmd_reg_add(&grcp_cmd, IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, payload.pld32); + } + break; + default: + break; + } + + grcp_len = (grcp_cmd.cmd_len - GRCP_HEADER) / 4; + *(u32 *)(grcp_cmd.cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(grcp_cmd.cmd + 14) = cpu_to_le16(grcp_len); + + iris_grcp_cmd[0].dchdr.dlen = grcp_cmd.cmd_len; + + panel_cmds.cmds = iris_grcp_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_grcp_cmd); + panel_cmds.link_state = DSI_LP_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); +} + + + +int iris_one_wired_cmd_init(struct mdss_dsi_ctrl_pdata *ctrl) +{ +#if defined(ONE_WIRED_CMD_VIA_RESET_GPIO) + if (!gpio_is_valid(ctrl->rst_gpio)) { + pr_err("%s:%d, reset line not configured\n", + __func__, __LINE__); + return FAILED; + } +#elif defined(ONE_WIRED_CMD_VIA_WAKEUP_GPIO) + if (!gpio_is_valid(ctrl->px_bp_gpio)) { + pr_err("%s:%d, reset line not configured\n", + __func__, __LINE__); + return FAILED; + } +#endif + +#if defined(ONE_WIRED_CMD_VIA_RESET_GPIO) + gpio_direction_output(ctrl->rst_gpio, 1); +#elif defined(ONE_WIRED_CMD_VIA_WAKEUP_GPIO) + gpio_direction_output(ctrl->px_bp_gpio, 0); +#endif + msleep(2); + + return SUCCESS; +} + +void iris_one_wired_cmd_send(struct mdss_dsi_ctrl_pdata *ctrl, int cmd) +{ +#define POR_CLOCK 180 /* 0.1 Mhz*/ + + int cnt = 0; + u32 start_end_delay = 0, pulse_delay = 0; + unsigned long flags; +#if defined(ONE_WIRED_CMD_VIA_RESET_GPIO) + if (!gpio_is_valid(ctrl->rst_gpio)) { + pr_err("%s:%d, reset line not configured\n", + __func__, __LINE__); + return; + } +#elif defined(ONE_WIRED_CMD_VIA_WAKEUP_GPIO) + if (!gpio_is_valid(ctrl->px_bp_gpio)) { + pr_err("%s:%d, reset line not configured\n", + __func__, __LINE__); + return; + } +#endif + start_end_delay = 16 * 16 * 16 * 10 / POR_CLOCK; /*us*/ + pulse_delay = 16 * 16 * 4 * 10 / POR_CLOCK; /*us*/ + + spin_lock_irqsave(&ctrl->iris_lock, flags); + for (cnt = 0; cnt < cmd; cnt++) { +#if defined(ONE_WIRED_CMD_VIA_RESET_GPIO) + gpio_set_value(ctrl->rst_gpio, 0); + udelay(pulse_delay); + gpio_set_value(ctrl->rst_gpio, 1); + udelay(pulse_delay); +#elif defined(ONE_WIRED_CMD_VIA_WAKEUP_GPIO) + gpio_set_value(ctrl->px_bp_gpio, 1); + udelay(30); + gpio_set_value(ctrl->px_bp_gpio, 0); + udelay(30); +#endif + } + spin_unlock_irqrestore(&ctrl->iris_lock, flags); + /*end*/ + udelay(start_end_delay); +} +#if 0 +int iris_power_clock_gate_on(struct msm_fb_data_type *mfd, int *pflag) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + u8 signal_mode = 0xff, mode_switch; + + if ((*pflag) || !gp_mfd->iris_conf.ready) + return true; + + mode_switch = iris_cfg->sf_mode_change_start + && ((IRIS_MODE_FRC_PREPARE == iris_cfg->sf_notify_mode) + || (IRIS_MODE_RFB_PREPARE == iris_cfg->sf_notify_mode) + || (IRIS_MODE_PT_PREPARE == iris_cfg->sf_notify_mode)); + + if (iris_info.update.pq_setting || iris_info.update.dbc_setting || iris_info.update.color_adjust + || iris_info.update.lce_setting || iris_info.update.cm_setting || mode_switch) { + if (iris_info.low_power) { + + signal_mode = iris_mipi_signal_mode_read(g_ctrl, DSI_LP_MODE); + if (signal_mode != 0) + return false; + + iris_reg_add(IRIS_SYS_ADDR + 0x0, 0x2cc00044); + iris_reg_add(IRIS_SYS_ADDR + 0x4, 0x00000280); + iris_reg_add(IRIS_SYS_ADDR + 0x8, 0xff000000); + iris_reg_add(IRIS_SYS_ADDR + 0x228, 0x00000003); + iris_reg_add(IRIS_SYS_ADDR + 0x218, 0x00000003); + iris_reg_add(IRIS_DPORT_ADDR + 0x0, 0xe2708007); + iris_reg_add(0xf104000c, 0x00000000); + iris_info.low_power = 0; + pr_info("clock on\n"); + } + if (iris_cfg->sf_mode_change_start) + *pflag = 2; + else if (*pflag != 2) + *pflag = 1; + pr_info("flag = %d\n", *pflag); + } + return true; +} + +void iris_low_power_mode_notify(struct msm_fb_data_type *mfd, int *pflag) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + + if (0 == *pflag) + return; + + if (2 == *pflag) { + pr_info("mode: %d, %d\n", iris_cfg->current_mode, iris_cfg->sf_notify_mode); + if (!(((IRIS_RFB_MODE == iris_cfg->current_mode) && (IRIS_MODE_RFB == iris_cfg->sf_notify_mode)) + || ((IRIS_PT_MODE == iris_cfg->current_mode) && (IRIS_MODE_PT == iris_cfg->sf_notify_mode)) + || ((IRIS_FRC_MODE == iris_cfg->current_mode) && (IRIS_MODE_FRC == iris_cfg->sf_notify_mode)))) + return; + } + *pflag = 0; + iris_reg_add(IRIS_MIPI_RX_ADDR + 0x4, 0x1); + iris_info.low_power = 1; + pr_info("enter low power mode\n"); +} +#endif + void iris_low_power_mode_set(struct mdss_dsi_ctrl_pdata *ctrl, bool enable) + { + u32 grcp_len = 0; + struct dsi_cmd_desc iris_grcp_cmd[] = { + {{ DTYPE_GEN_LWRITE, 1, 0, 0, 0, CMD_PKT_SIZE}, grcp_cmd.cmd}, + }; + struct dsi_panel_cmds panel_cmds; + + memset(&grcp_cmd, 0, sizeof(grcp_cmd)); + memcpy(grcp_cmd.cmd, grcp_header, GRCP_HEADER); + grcp_cmd.cmd_len = GRCP_HEADER; + + if (enable) { + /* power down edram1~8 */ + iris_cmd_reg_add(&grcp_cmd, 0xf104000c, 0xfffffffe); + + /*disable DPHY*/ + iris_cmd_reg_add(&grcp_cmd, 0xf0160000, 0x00000000); + iris_cmd_reg_add(&grcp_cmd, 0xf0160030, 0x00000000); + iris_cmd_reg_add(&grcp_cmd, 0xf0180000, 0x0a00c138); + iris_cmd_reg_add(&grcp_cmd, 0xf0180004, 0x00000000); + iris_cmd_reg_add(&grcp_cmd, 0xf0180018, 0x00000101); + iris_cmd_reg_add(&grcp_cmd, 0xf01c0000, 0x0a00c138); + iris_cmd_reg_add(&grcp_cmd, 0xf01c0004, 0x00000000); + iris_cmd_reg_add(&grcp_cmd, 0xf01c0004, 0x00000101); + + /* set clock source to XCLK */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000210, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000214, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000218, 0x00000f0b); + iris_cmd_reg_add(&grcp_cmd, 0xf000021c, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000228, 0x0000070b); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000000); + + /* power down PLL */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000140, 0x00000003); + iris_cmd_reg_add(&grcp_cmd, 0xf0000150, 0x00002003); + iris_cmd_reg_add(&grcp_cmd, 0xf0000160, 0x00000003); + + /* gate off clock */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000000, 0x3fc87fff); + iris_cmd_reg_add(&grcp_cmd, 0xf0000004, 0x00007f8e); + + } else { + /* gate on clock */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000000, 0x20000000); + iris_cmd_reg_add(&grcp_cmd, 0xf0000004, 0x00000000); + iris_cmd_reg_add(&grcp_cmd, 0xf0000248, 0x02008000); + + /* power up PLL */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000140, 0x00000002); + iris_cmd_reg_add(&grcp_cmd, 0xf0000150, 0x00002002); + iris_cmd_reg_add(&grcp_cmd, 0xf0000160, 0x00000002); + + /* restore clock source */ + iris_cmd_reg_add(&grcp_cmd, 0xf0000210, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000214, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000218, 0x00000003); + iris_cmd_reg_add(&grcp_cmd, 0xf000021c, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000228, 0x00000003); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0000014, 0x00000000); + + /* enable DPHY */ + iris_cmd_reg_add(&grcp_cmd, 0xf0160030, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0160000, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0180004, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf0180000, 0x0a00c139); + iris_cmd_reg_add(&grcp_cmd, 0xf01c0004, 0x00000001); + iris_cmd_reg_add(&grcp_cmd, 0xf01c0000, 0x0a00c139); + + /* power up edram1~8 */ + iris_cmd_reg_add(&grcp_cmd, 0xf104000c, 0x00000000); + } + + grcp_len = (grcp_cmd.cmd_len - GRCP_HEADER) / 4; + *(u32 *)(grcp_cmd.cmd + 8) = cpu_to_le32(grcp_len + 1); + *(u16 *)(grcp_cmd.cmd + 14) = cpu_to_le16(grcp_len); + + iris_grcp_cmd[0].dchdr.dlen = grcp_cmd.cmd_len; + + pr_info("iris low power mode: %d\n", enable); + + panel_cmds.cmds = iris_grcp_cmd; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris_grcp_cmd); + panel_cmds.link_state = DSI_LP_MODE; + iris_dsi_cmds_send(ctrl, &panel_cmds); + + } + +#if 0 +void iris_mipitx_te_source_change(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + u32 tx_ch, mipitx_addr = IRIS_MIPI_TX_ADDR; + + for (tx_ch = 0; tx_ch < (pwork_mode->tx_ch + 1); tx_ch++) { + #ifdef MIPI_SWAP + mipitx_addr -= tx_ch * IRIS_MIPI_ADDR_OFFSET; + #else + mipitx_addr += tx_ch * IRIS_MIPI_ADDR_OFFSET; + #endif + iris_reg_add(mipitx_addr + TE_FLOW_CTRL, 0x00000100); + } + iris_reg_add(IRIS_SYS_ADDR + ALT_CTRL0, 0x00008000); +} + +void iris_abypass_switch_state_init(int mode) +{ + if (!iris_info.abypss_ctrl.analog_bypass_enable) + return; + + if (IRIS_MODE_PT2BYPASS == mode) { + iris_info.abypss_ctrl.pt_to_abypass_enable = 1; + iris_info.abypss_ctrl.abypass_to_pt_enable = 0; + iris_info.abypss_ctrl.abypss_switch_state = TTL_CMD_BYPASS_STATE; + iris_info.abypss_ctrl.frame_delay = 0; + + pr_info("pt->analog bypass\n"); + } else if (IRIS_MODE_BYPASS2PT == mode) { + iris_info.abypss_ctrl.pt_to_abypass_enable = 0; + iris_info.abypss_ctrl.abypass_to_pt_enable = 1; + iris_info.abypss_ctrl.abypss_switch_state = LOW_POWER_EXIT_STATE; + iris_info.abypss_ctrl.frame_delay = 0; + + pr_info("analog bypass->pt\n"); + } +} + int iris_pt_to_abypass_switch(struct mdss_dsi_ctrl_pdata *ctrl) + { + + int *pswitch_state = &iris_info.abypss_ctrl.abypss_switch_state; + int *pframe_delay = &iris_info.abypss_ctrl.frame_delay; + int bypass_mode = BYPASS_CMD, pwil_mode = PWIL_CMD, ret = false; + + if (!iris_info.abypss_ctrl.pt_to_abypass_enable) + return ret; + + if (iris_info.work_mode.rx_mode) { + bypass_mode = BYPASS_CMD; + pwil_mode = PWIL_CMD; + } else { + bypass_mode = BYPASS_VIDEO; + pwil_mode = PWIL_VIDEO; + } + + + /*if state switch need delay several video frames*/ + if (*pframe_delay > 0) + *pframe_delay -= 1; + if (*pframe_delay > 0) + return ret; + + + switch (*pswitch_state) { + case PASS_THROUGH_STATE: + iris_pwil_mode_set(ctrl, PT_MODE, DSI_HS_MODE); + *pswitch_state = TTL_CMD_BYPASS_STATE; + *pframe_delay = 1; + break; + case TTL_CMD_BYPASS_STATE: + /* TTL/CMD bypass */ + iris_mipirx_mode_set(ctrl, bypass_mode, DSI_HS_MODE); + *pswitch_state = ANALOG_BYPASS_ENTER_STATE; + *pframe_delay = 1; + break; + case ANALOG_BYPASS_ENTER_STATE: + /* analog bypass */ + iris_one_wired_cmd_send(ctrl, BYPASS_MODE_CHANGE); + *pswitch_state = LOW_POWER_ENTER_STATE; + *pframe_delay = 1; + break; + case LOW_POWER_ENTER_STATE: + /* enter low power mode */ + iris_mipirx_mode_set(ctrl, pwil_mode, DSI_LP_MODE); + + + iris_low_power_mode_set(ctrl, true); + *pswitch_state = ANALOG_BYPASS_STATE; + *pframe_delay = 1; + break; + case ANALOG_BYPASS_STATE: + *pframe_delay = 0; + iris_info.abypss_ctrl.abypass_status = ANALOG_BYPASS_MODE; + iris_info.abypss_ctrl.pt_to_abypass_enable = 0; + ret = true; + break; + default: + break; + } + pr_info("state: %d, delay: %d\n", *pswitch_state, *pframe_delay); + + return ret; +} + +int iris_abypass_to_pt_switch(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int *pswitch_state = &iris_info.abypss_ctrl.abypss_switch_state; + int *pframe_delay = &iris_info.abypss_ctrl.frame_delay; + + int bypass_mode = BYPASS_CMD, pwil_mode = PWIL_CMD, ret = false; + + if (!iris_info.abypss_ctrl.abypass_to_pt_enable) + return ret; + + if (iris_info.work_mode.rx_mode) { + bypass_mode = BYPASS_CMD; + pwil_mode = PWIL_CMD; + } else { + + bypass_mode = BYPASS_VIDEO; + pwil_mode = PWIL_VIDEO; + } + /*if state switch need delay several video frames*/ + if (*pframe_delay > 0) + *pframe_delay -= 1; + if (*pframe_delay > 0) + return ret; + + switch (*pswitch_state) { + case LOW_POWER_EXIT_STATE: + /* exit low power mode */ + iris_low_power_mode_set(ctrl, false); + iris_mipirx_mode_set(ctrl, bypass_mode, DSI_LP_MODE); + + *pswitch_state = ANALOG_BYPASS_EXIT_STATE; + *pframe_delay = 1; + break; + case ANALOG_BYPASS_EXIT_STATE: + /* analog bypass */ + iris_one_wired_cmd_send(ctrl, BYPASS_MODE_CHANGE); + + *pswitch_state = PASS_THROUGH_STATE; + *pframe_delay = 1; + break; + case PASS_THROUGH_STATE: + iris_mipirx_mode_set(ctrl, pwil_mode, DSI_HS_MODE); + iris_pwil_mode_set(ctrl, PT_MODE, DSI_HS_MODE); + /* change TX's TE source */ + if (iris_info.work_mode.tx_mode) + iris_mipitx_te_source_change(); + + if (iris_info.abypss_ctrl.abypass_debug) { + iris_info.abypss_ctrl.abypass_debug = false; + *pswitch_state = RFB_STATE; + *pframe_delay = 1; + } else { + *pframe_delay = 0; + iris_info.abypss_ctrl.abypass_status = PASS_THROUGH_MODE; + iris_info.abypss_ctrl.abypass_to_pt_enable = 0; + ret = true; + } + break; + case RFB_STATE: + iris_pwil_mode_set(ctrl, RFB_MODE, DSI_HS_MODE); + *pframe_delay = 0; + iris_info.abypss_ctrl.abypass_status = PASS_THROUGH_MODE; + iris_info.abypss_ctrl.abypass_to_pt_enable = 0; + ret = true; + break; + default: + break; + } + pr_info("state: %d, delay: %d\n", *pswitch_state, *pframe_delay); + + return ret; +} + +void iris_abypass_switch_proc(struct mdss_dsi_ctrl_pdata *ctrl) +{ + if (iris_info.abypss_ctrl.analog_bypass_enable) { + if (iris_info.abypss_ctrl.abypass_status == PASS_THROUGH_MODE) + iris_pt_to_abypass_switch(ctrl); + if (iris_info.abypss_ctrl.abypass_status == ANALOG_BYPASS_MODE) + iris_abypass_to_pt_switch(ctrl); + } + + +} + +void iris_fw_download_cont_splash(struct mdss_dsi_ctrl_pdata *ctrl, bool video_freeze) +{ + struct mdss_mdp_ctl *ctl = mfd_to_ctl(gp_mfd); + + + pr_info("off video\n"); + if (iris_info.work_mode.rx_mode) { + if (video_freeze) { + mdss_mdp_lock(gp_mfd, 1); + msleep(100); + } + } else { + mdss_mdp_time_engine_ctrl(ctl, 0); + + /* reset dsi */ + mdss_dsi_ctrl_setup(ctrl); + mdss_dsi_sw_reset(ctrl, true); + + /* switch to cmd mode */ + iris_mipirx_mode_set(ctrl, PWIL_CMD, DSI_HS_MODE); + } + + /* firmware download */ + pr_info("firmware download\n"); + iris_info.firmware_info.fw_dw_result = + iris2p_firmware_download(ctrl, gp_mfd, IRIS_FIRMWARE_NAME); + if (SUCCESS == iris_info.firmware_info.fw_dw_result) { + iris_ctrl_cmd_send(ctrl, ITCM_COPY + REMAP, DSI_HS_MODE); + gp_mfd->iris_conf.ready = true; + } + + msleep(200); + if (iris_info.work_mode.rx_mode) { + if (video_freeze) { + mdss_mdp_lock(gp_mfd, 0); + } + } else { + mdss_mdp_time_engine_ctrl(ctl, 1); + + /* restore to video mode */ + iris_mipirx_mode_set(ctrl, PWIL_VIDEO, DSI_HS_MODE); + } + pr_info("on video\n"); + +} + +static ssize_t iris_one_wired_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + + if (SUCCESS == iris_one_wired_cmd_init(g_ctrl)) + iris_one_wired_cmd_send(g_ctrl, val); + + pr_info("one wired %u\n", (u32)val); + + return count; +} + +static ssize_t iris_fw_dw_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + +// iris_fw_download_cont_splash(g_ctrl, 1); + + return count; +} + +static ssize_t iris_abypass_switch_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned long val; + static int cnt = 0; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + + if (!iris_info.abypss_ctrl.analog_bypass_enable) { + pr_info("analog bypass is not enabled\n"); + return count; + } + + iris_info.abypss_ctrl.abypass_debug = true; + cnt++; + if (val) { + iris_info.abypss_ctrl.pt_to_abypass_enable = 1; + iris_info.abypss_ctrl.abypass_to_pt_enable = 0; + iris_info.abypss_ctrl.abypss_switch_state = PASS_THROUGH_STATE; + iris_info.abypss_ctrl.frame_delay = 0; + pr_err("pt->analog bypass, %d\n", cnt); + + } else { + iris_info.abypss_ctrl.pt_to_abypass_enable = 0; + iris_info.abypss_ctrl.abypass_to_pt_enable = 1; + iris_info.abypss_ctrl.abypss_switch_state = LOW_POWER_EXIT_STATE; + iris_info.abypss_ctrl.frame_delay = 0; + pr_err("analog bypass->pt, %d\n", cnt); + } + + return count; +} + +static const struct file_operations iris_one_wired_fops = { + .open = simple_open, + .write = iris_one_wired_write, +}; + +static const struct file_operations iris_fw_dw_fops = { + .open = simple_open, + .write = iris_fw_dw_write, +}; + +static const struct file_operations iris_abypss_switch_fops = { + .open = simple_open, + .write = iris_abypass_switch_write, +}; + +int iris2p_debugfs_init(struct msm_fb_data_type *mfd) +{ + if (mfd->index != 0) + return 0; + if (!(mfd->panel.type == MIPI_VIDEO_PANEL || mfd->panel.type == MIPI_CMD_PANEL)) + return 0; + + gp_mfd = mfd; + pr_info("gp_mfd %p\n", gp_mfd); + pr_info("%s:%d: mfd->panel.type: %i mfd->panel.id: %i\n", __func__, __LINE__, mfd->panel.type, mfd->panel.id); + + if (debugfs_create_file("iris_one_wired", 0644, NULL, mfd, + &iris_one_wired_fops) == NULL) { + pr_err("%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + if (debugfs_create_file("iris_fw_dw", 0644, NULL, mfd, + &iris_fw_dw_fops) == NULL) { + pr_err("%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + if (debugfs_create_file("iris_abypss_switch", 0644, NULL, mfd, + &iris_abypss_switch_fops) == NULL) { + pr_err("%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + + return 0; +} +#endif + diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.h new file mode 100644 index 000000000000..fb43bdd3d6e9 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup.h @@ -0,0 +1,21 @@ + +#ifndef MDSS_DSI_IRISP_API_H +#define MDSS_DSI_IRISP_API_H + + +extern void iris_init(struct mdss_dsi_ctrl_pdata *ctrl); +extern void iris_lightup(struct mdss_dsi_ctrl_pdata *ctrl); +extern void iris_init_params_parse(struct device_node *np, struct mdss_dsi_ctrl_pdata *ctrl_pdata); +extern void iris_init_cmd_setup(struct mdss_dsi_ctrl_pdata *ctrl_pdata); +extern void iris_lightoff(struct mdss_dsi_ctrl_pdata *ctrl); +extern int iris2p_debugfs_init(struct msm_fb_data_type *mfd); +extern void iris_fw_download_cont_splash(struct mdss_dsi_ctrl_pdata *ctrl, bool video_freeze); +extern void iris_abypass_switch_state_init(int mode); +extern void iris_abypass_switch_proc(struct mdss_dsi_ctrl_pdata *ctrl); +extern int iris_pt_to_abypass_switch(struct mdss_dsi_ctrl_pdata *ctrl); +extern int iris_abypass_to_pt_switch(struct mdss_dsi_ctrl_pdata *ctrl); +extern void iris_panel_cmd_passthrough(struct mdss_dsi_ctrl_pdata *ctrl, struct dcs_cmd_req *cmdreq); +extern int iris_power_clock_gate_on(struct msm_fb_data_type *mfd, int *pflag); +extern void iris_low_power_mode_notify(struct msm_fb_data_type *mfd, int *pflag); +#endif + diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup_priv.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup_priv.h new file mode 100644 index 000000000000..c6a39c5aa8b9 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_lightup_priv.h @@ -0,0 +1,370 @@ +/* Copyright (c) 2013, Pixelworks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef MDSS_DSI_IRISP_H +#define MDSS_DSI_IRISP_H + +#include +#include "mdss_dsi_iris2p_def.h" + + +#define CMD_PKT_SIZE 512 +#define GRCP_HEADER 16 +#define INIT_CMD_NUM 2 + + + +#define FPGA_DEBUG +#ifdef FPGA_DEBUG +#define WAKEUP_TIME 500 +#define CMD_PROC 10 +#define INIT_INT 100 +#define INIT_WAIT 100 +#else +#define WAKEUP_TIME 50 +#define CMD_PROC 2 +#define INIT_INT 100 +#define INIT_WAIT 20 +#endif + +//#define MIPI_SWAP +#define READ_CMD_ENABLE +//#define NEW_WORKFLOW +//#define ONE_WIRED_CMD_VIA_RESET_GPIO +#define ONE_WIRED_CMD_VIA_WAKEUP_GPIO +//#define EFUSE_REWRITE + +#define IRIS_SYS_ADDR 0xF0000000 +#ifdef MIPI_SWAP +#define IRIS_MIPI_RX_ADDR 0xF0140000 +#define IRIS_MIPI_TX_ADDR 0xF01c0000 +#else +#define IRIS_MIPI_RX_ADDR 0xF0100000 +#define IRIS_MIPI_TX_ADDR 0xF0180000 +#endif +#define IRIS_PROXY_ADDR 0xF0040000 +#define IRIS_PWIL_ADDR 0xF1240000 +#define IRIS_DTG_ADDR 0xF1200000 +#define IRIS_DPORT_ADDR 0xF1220000 + +/* SYS register */ +#define CLKMUX_CTRL 0x0c +#define CLKDIV_CTRL 0x10 +#define PPLL_B_CTRL1 0x144 +#define PPLL_B_CTRL2 0x148 +#define DPLL_B_CTRL1 0x154 +#define DPLL_B_CTRL2 0x158 +#define PLL_CTRL 0x200 +#define ALT_CTRL0 0x248 +#define DFT_EFUSE_CTRL 0x10000 +#define DFT_EFUSE_CTRL_1 0x10004 + + +/*DTG register*/ +#define HSCTRL1 0x04 +#define VSCTRL0 0x10 +#define VSCTRL1 0x14 +#define DTG_CTRL 0x20 +#define EVS_DLY 0x2c +#define EVS_NEW_DLY 0x30 +#define DTG_DELAY 0x34 +#define TE_CTRL 0x4c +#define TE_CTRL_1 0x50 +#define TE_CTRL_2 0x54 +#define TE_CTRL_3 0x58 +#define TE_CTRL_4 0x5c +#define TE_CTRL_5 0xac +#define DTG_CTRL_1 0x70 +#define VFP_CTRL_0 0x7c +#define VFP_CTRL_1 0x80 +#define DVS_CTRL 0x90 +#define TE_DLY 0x9c +#define TE_DLY_1 0xb0 +#define DTG_RESERVE 0xa8 +#define REGSEL 0x10000 + +/*MIPI_RX register*/ +#define DBI_HANDLER_CTRL 0x0000c +#define FRAME_COLUMN_ADDR 0x00018 +#define ABNORMAL_COUNT_THRES 0x0001c +#define DEVICE_READY 0x20000 +#define DSI_FUNCTIONAL_PROGRAMMING 0x2000c +#define EOT_ECC_CRC_DISABLE 0x20024 +#define DATA_LANE_TIMING_PARAMETER 0x2002c +#define RESET_ENABLE_DFE 0x20030 +#define DPI_SYNC_COUNT 0x20058 + +/*MIPI_TX register*/ +#define DSI_TX_CTRL 0x00000 +#define DPHY_CTRL 0x00004 +#define MANUAL_BYPASS_CTRL 0x00008 +#define TE_FLOW_CTRL 0x00018 +#define DUAL_CH_CTRL 0x00020 +#define HS_TX_TIMER 0x00024 +#define BTA_LP_TIMER 0x00028 +#define INITIALIZATION_RESET_TIMER 0x0002c +#define TX_RESERVED_0 0x00030 +#define DPHY_TIMING_MARGIN 0x00040 +#define DPHY_LP_TIMING_PARA 0x00050 +#define DPHY_DATA_LANE_TIMING_PARA 0x00054 +#define DPHY_CLOCK_LANE_TIMING_PARA 0x00058 +#define DPHY_PLL_PARA 0x0005c +#define DPHY_TRIM_1 0x00064 +#define WR_PACKET_HEADER_OFFS 0x1c010 +#define WR_PACKET_PAYLOAD_OFFS 0x1c014 +#define RD_PACKET_DATA_OFFS 0x1c018 + + +#define IRIS_MIPI_ADDR_OFFSET 0x40000 +#define IRIS_GRCP_BUFFER_ADDR 0xf0212C00 +#define IRIS_GRCP_CTRL_ADDR 0xf0200000 + +#define PWIL_TAG(a, b, c, d) d, c, b, a +#define PWIL_U32(x) \ + (__u8)(((x) ) & 0xff), \ + (__u8)(((x) >> 8) & 0xff), \ + (__u8)(((x) >> 16) & 0xff), \ + (__u8)(((x) >> 24) & 0xff) + +#define PWIL_U16(x) \ + (__u8)(((x) ) & 0xff), \ + (__u8)(((x) >> 8 ) & 0xff) + +#define FW_COL_CNT 85 +#define FW_DW_CMD_CNT 16 +#define DSI_DMA_TX_BUF_SIZE SZ_64K +#define DCS_WRITE_MEM_START 0x2C +#define DCS_WRITE_MEM_CONTINUE 0x3C + +#define IRIS_FIRMWARE_NAME "iris2p.fw" + + + +enum result { + FAILED = -1, + SUCCESS = 0, +}; + + +enum mipi_rx_mode { + MCU_VIDEO = 0, + MCU_CMD = 1, + PWIL_VIDEO = 2, + PWIL_CMD = 3, + BYPASS_VIDEO = 4, + BYPASS_CMD = 5, +}; + +enum romcode_ctrl { + CONFIG_DATAPATH = 1, + ENABLE_DPORT = 2, + ITCM_COPY = 4, + REMAP = 8, +}; + +enum pwil_mode { + PT_MODE, + RFB_MODE, + BIN_MODE, +}; + +enum iris_mipi_mode { + MIPI_VIDEO_MODE = 0x0, + MIPI_CMD_MODE = 0x01, +}; + +enum iris_onewired_cmd { + RX0_POWER_UP = 1, + BYPASS_MODE_CHANGE = 2, + RX0_POWER_DOWN = 3, + RX0_RESET = 4, + FORCE_WORK_MODE_SWITCH = 5, + REVERSED = 6, +}; + +enum iris_abypass_status { + ANALOG_BYPASS_MODE = 0, + PASS_THROUGH_MODE, +}; + +enum iris_abypss_switch_state { + PASS_THROUGH_STATE = 0, + TTL_CMD_BYPASS_STATE, + ANALOG_BYPASS_ENTER_STATE, + RX0_POWER_DOWN_STATE, + ANALOG_BYPASS_STATE, + RX0_POWER_UP_STATE , + RFB_STATE, + ANALOG_BYPASS_EXIT_STATE, + LOW_POWER_ENTER_STATE, + LOW_POWER_EXIT_STATE, +}; + +struct iris_work_mode { + u32 rx_mode:1; /* 0-video/1-cmd */ + u32 rx_ch:1; /* 0-single/1-dual */ + u32 rx_dsc:1; /* 0-non DSC/1-DSC */ + u32 rx_pxl_mode:1; /*interleave/left-right */ + u32 reversed0:12; + + u32 tx_mode:1; /* 0-video/1-cmd */ + u32 tx_ch:1; /* 0-single/1-dual */ + u32 tx_dsc:1; /* 0-non DSC/1-DSC */ + u32 tx_pxl_mode:1; /* interleave/left-right */ + u32 reversed1:12; +}; + +struct iris_timing_info { + u16 hfp; + u16 hres; + u16 hbp; + u16 hsw; + u16 vfp; + u16 vres; + u16 vbp; + u16 vsw; + u16 fps; +}; + +struct iris_setting_disable_info { + u32 last_frame_repeat_cnt; + + u32 dbc_dlv_sensitivity_disable_val; + u32 dbc_quality_disable_val; + + u32 pq_peaking_disable_val; + u32 pq_peaking_demo_disable_val; + int pq_gamma_disable_val; + u32 pq_contrast_disable_val; + + u32 lce_mode_disable_val; + + u32 color_adjust_disable_val; + + u32 cm_c6axes_disable_val; + u32 cm_c3d_disable_val; + u32 cm_ftcen_disable_val; +}; + +struct iris_setting_info { + u32 dpll_clock; + u32 dpll0; + u32 dpll1; + u32 dpll2; + u32 ppll1; + u32 ppll2; + u32 tx_escclk_setting; + + u32 mipirx_dsi_functional_program; + u32 mipirx_eot_ecc_crc_disable; + u32 mipirx_data_lane_timing_param; + + u32 mipitx_dsi_tx_ctrl; + u32 mipitx_hs_tx_timer; + u32 mipitx_bta_lp_timer; + u32 mipitx_initialization_reset_timer; + u32 mipitx_dphy_timing_margin; + u32 mipitx_lp_timing_para; + u32 mipitx_data_lane_timing_param; + u32 mipitx_clk_lane_timing_param; + u32 mipitx_dphy_pll_para; + u32 mipitx_dphy_trim_1; + + int delta_period_max; + int delta_period_min; + + struct iris_setting_disable_info disable_info; + struct quality_setting quality_def; + struct quality_setting quality_cur; + //TODO + u8 dbc_mode; +}; + +struct iris_dsc_info { + u16 slice_number; + u16 slice_height; + u16 bpp; +}; + +struct iris_fw_info { + u32 firmware_size; + int fw_dw_result; +}; + +struct iris_tx_switch_cmd { + struct dsi_panel_cmds mipitx_cmdmode_cmds; + struct dsi_panel_cmds mipitx_vid2cmd_cmds; + struct dsi_panel_cmds mipitx_cmd2vid_cmds; +}; + +struct iris_intf_switch_info { + u8 rx_switch_enable; + u8 rx_current_mode; + + u8 tx_switch_enable; + u8 tx_current_mode; +}; + + +struct iris_abypass_ctrl { + bool analog_bypass_enable; + bool abypass_status; + bool abypass_debug; + bool abypass_to_pt_enable; + bool pt_to_abypass_enable; + int abypss_switch_state; + u32 base_time; + int frame_delay; +}; + +struct iris_info_t { + struct iris_work_mode work_mode; + struct iris_timing_info input_timing; + struct iris_timing_info output_timing; + struct iris_setting_info setting_info; + struct iris_dsc_info input_dsc; + struct iris_dsc_info output_dsc; + struct iris_fw_info firmware_info; + struct fi_demo_win fi_demo_win_info; + struct peaking_demo_win peaking_demo_win_info; + struct cm_demo_win cm_demo_win_info; + struct iris_conf_update update; + struct iris_tx_switch_cmd tx_switch_cmd; + struct iris_intf_switch_info intf_switch_info; + struct iris_abypass_ctrl abypss_ctrl; + int low_power; +}; + +struct iris_grcp_cmd { + char cmd[CMD_PKT_SIZE]; + int cmd_len; +}; + +struct iris_rom_status { + u8 init_done : 1; + u8 config_datapath_done : 1; + u8 enable_dport_done : 1; + u8 itcm_copy_done : 1; + u8 remap_done : 1; + u8 reserved : 2; + u8 Romcode : 1; +}; + +typedef union +{ + struct iris_rom_status Rom_Sts; + u8 value; +} uPowerMode; + + +#endif + + diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.c b/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.c new file mode 100644 index 000000000000..949594cfaaa9 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.c @@ -0,0 +1,1515 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_fb.h" +#include "mdss_dsi.h" +#include "mdss_dsi_iris2p.h" +#include "mdss_i2c_iris.h" +#include "mdss_dsi_iris2p_def.h" +#include "mdss_dsi_iris2p_extern.h" +#include "mdss_dsi_iris2p_mode_switch.h" +#include "mdss_dsi_iris2p_lightup.h" +#include "mdss_debug.h" + +static enum iris_frc_prepare_state eiris_frc_prepare_state; +static enum iris_frc_cancel_state eiris_frc_cancel_state; +static enum iris_mode_rfb2frc_state eiris_mode_rfb2frc_state; +static enum iris_rfb_prepare_state eiris_rfb_prepare_state; +static enum iris_mode_frc2rfb_state eiris_mode_frc2rfb_state; +static enum iris_pt_prepare_state eiris_pt_prepare_state; +static enum iris_mode_pt2rfb_state eiris_mode_pt2rfb_state; +static enum iris_mode_rfb2pt_state eiris_mode_rfb2pt_state; +static int iris_mode_bypass_switch(int val); + +static void iris_memc_path_commands_update(void) +{ + struct iris_fun_enable *enable = (struct iris_fun_enable *)&g_mfd->iris_conf.frc_path; + + if (g_mfd->iris_conf.true_cut_enable) + enable->true_cut_en = 1; + else + enable->true_cut_en = 0; + + enable->nrv_drc_en = 0; + if (g_mfd->iris_conf.nrv_enable) + enable->nrv_drc_en = 1; +#ifdef CONFIG_IRIS2P_DRC_SUPPORT + if (g_mfd->iris_conf.drc_enable) + enable->nrv_drc_en = 1; +#endif + if (iris_is_peaking_setting_disable() && iris_is_cm_setting_disable()) + enable->pp_en = 0; + else + enable->pp_en = 1; + enable->use_efifo_en = 0; + enable->psr_post_sel = 0; + enable->frc_data_format = 1; + enable->capt_bitwidth = 0; + enable->psr_bitwidth = 0; +#if defined(FPGA_PLATFORM) + // TODO: enable all feature to do test + enable->dbc_lce_en = 1; + enable->dpp_en = 1; +#else + if (!iris_is_lce_setting_disable() || !iris_is_dbc_setting_disable()) + enable->dbc_lce_en = 1; + else + enable->dbc_lce_en = 0; +#endif + *(u32 *)(iris_memc_enter_cmds + 20) = cpu_to_le32(g_mfd->iris_conf.frc_path); +} + +static void iris_pt_path_commands_update(void) +{ + struct iris_fun_enable *enable = (struct iris_fun_enable *)&g_mfd->iris_conf.pt_path; + + if (iris_is_peaking_setting_disable() && iris_is_cm_setting_disable()) + enable->pp_en = 0; + else + enable->pp_en = 1; + enable->use_efifo_en = 0; + enable->psr_post_sel = 0; + enable->frc_data_format = 1; + enable->capt_bitwidth = 0; + enable->psr_bitwidth = 0; +#if defined(FPGA_PLATFORM) + // TODO: enable all feature to do test + enable->dbc_lce_en = 1; + enable->dpp_en = 1; +#else + if (!iris_is_lce_setting_disable() || !iris_is_dbc_setting_disable()) + enable->dbc_lce_en = 1; + else + enable->dbc_lce_en = 0; +#endif + *(u32 *)(iris_pt_enter_cmds + 20) = cpu_to_le32(g_mfd->iris_conf.pt_path); +} + +static void iris_rfb_path_commands_update(void) +{ + struct iris_fun_enable *enable = (struct iris_fun_enable *)&g_mfd->iris_conf.rfb_path; + + if (iris_is_peaking_setting_disable() && iris_is_cm_setting_disable()) + enable->pp_en = 0; + else + enable->pp_en = 1; + enable->use_efifo_en = 0; + enable->psr_post_sel = 1; + enable->frc_data_format = 1; + enable->capt_bitwidth = 0; + enable->psr_bitwidth = 0; +#if defined(FPGA_PLATFORM) + // TODO: enable all feature to do test + enable->dbc_lce_en = 1; + enable->dpp_en = 1; +#else + if (!iris_is_lce_setting_disable() || !iris_is_dbc_setting_disable()) + enable->dbc_lce_en = 1; + else + enable->dbc_lce_en = 0; +#endif + *(u32 *)(iris_rfb_enter_cmds + 20) = cpu_to_le32(g_mfd->iris_conf.rfb_path); +} + +static int iris_memc_reg_write(struct msm_fb_data_type *mfd, struct fi_demo_win fi_demo_win_para) +{ + int endx, endy, startx, starty; + static int hres, vres; + struct iris_config *iris_cfg = &mfd->iris_conf; + int displaywidth = (iris_info.work_mode.tx_ch ? iris_info.output_timing.hres * 2 : iris_info.output_timing.hres); + + if ((lp_memc_timing[0] != hres) || (lp_memc_timing[1] != vres)) { + iris_info.update.demo_win_fi = true; + hres = lp_memc_timing[0]; + vres = lp_memc_timing[1]; + } + + pr_debug("iris_cfg->nrv_enable: %d\n", iris_cfg->nrv_enable); + if (iris_info.update.demo_win_fi && (iris_info.setting_info.quality_cur.pq_setting.memcdemo == 5)) { + iris_info.update.demo_win_fi = false; + if (!iris_cfg->nrv_enable) { + startx = iris_info.fi_demo_win_info.startx * lp_memc_timing[0] / displaywidth; + starty = iris_info.fi_demo_win_info.starty * lp_memc_timing[1] / iris_info.output_timing.vres; + endx = iris_info.fi_demo_win_info.endx * lp_memc_timing[0] / displaywidth; + endy = iris_info.fi_demo_win_info.endy * lp_memc_timing[1] / iris_info.output_timing.vres; + + if (endy + iris_info.fi_demo_win_info.borderwidth >= lp_memc_timing[1]) + endy = lp_memc_timing[1] - iris_info.fi_demo_win_info.borderwidth; + + pr_debug("iris: %s: startx = %d, starty = %d, endx = %d, endy = %d, lp_memc_timing[0] = %d, lp_memc_timing[1] = %d.\n", + __func__, startx, starty, endx, endy, lp_memc_timing[0], lp_memc_timing[1]); + fi_demo_win_para.colsize = (startx & 0xfff) | ((endx & 0xfff) << 16); + fi_demo_win_para.rowsize = (starty & 0xfff) | ((endy & 0xfff) << 16); + } + + iris_reg_add(FI_DEMO_COL_SIZE, fi_demo_win_para.colsize); + iris_reg_add(FI_DEMO_MODE_RING, fi_demo_win_para.color); + iris_reg_add(FI_DEMO_ROW_SIZE, fi_demo_win_para.rowsize); + iris_reg_add(FI_DEMO_MODE_CTRL, fi_demo_win_para.modectrl); + iris_reg_add(FI_SHADOW_UPDATE, 1); + } + return 0; +} + +static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + if (pcmds->link_state == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + + +void iris_dms_config(struct mdss_dsi_ctrl_pdata *ctrl) +{ + char dms_pwil_conf[] = { + PWIL_TAG('P', 'W', 'I', 'L'), + PWIL_TAG('G', 'R', 'C', 'P'), + PWIL_U32(0x00000007), // valid word number + 0x00, // burst mode + 0x00, // reserved + PWIL_U16(0x06), // burst length + PWIL_U32(IRIS_PWIL_ADDR + 0x0004), //PWIL ctrl1 confirm transfer mode and cmd mode + PWIL_U32(0x00082018), + PWIL_U32(IRIS_DTG_ADDR + 0x0070), //DTG_CTRL_1/CMD_MODE_EN + PWIL_U32(0x00000001), + PWIL_U32(IRIS_DTG_ADDR + 0x10000), //DTG/REGSEL + PWIL_U32(0x00000001), + }; + + struct dsi_cmd_desc iris2_dms_restore[] = { + { {DTYPE_GEN_LWRITE, 1, 0, 0, 0, sizeof(dms_pwil_conf)}, dms_pwil_conf} + }; + struct dsi_panel_cmds panel_cmds; + + pr_info("%s:, rx_mode=%d\n", __func__, iris_info.work_mode.rx_mode); + //confirm pwil work mode, video or cmd. + if (MIPI_VIDEO_MODE == iris_info.work_mode.rx_mode) + dms_pwil_conf[20] = 0x18; + else + dms_pwil_conf[20] = 0x1a; + + if (0 != iris_info.work_mode.rx_ch) + dms_pwil_conf[20] |= 0x01; //dual channel + + //confirm DTG_CTRL_1/CMD_MODE_EN + if (MIPI_VIDEO_MODE == iris_info.work_mode.rx_mode) + dms_pwil_conf[28] = 0x00; + else + dms_pwil_conf[28] = 0x01; + + panel_cmds.cmds = iris2_dms_restore; + panel_cmds.cmd_cnt = ARRAY_SIZE(iris2_dms_restore); + panel_cmds.link_state = DSI_HS_MODE; + mdss_dsi_panel_cmds_send(ctrl, &panel_cmds); + + pr_info("%s:-\n", __func__); +} + +void iris_mipi_tx_cmds_build(struct msm_fb_data_type *mfd, struct dsi_panel_cmds *pcmds) +{ + union iris_mipi_tx_cmd_header header; + union iris_mipi_tx_cmd_payload payload; + u32 i = 0, j = 0; + u32 cmd_cnt = pcmds->cmd_cnt; + + pr_info("%s, cmd_cnt=%d\n", __func__, cmd_cnt); + while (i < cmd_cnt) { + memset(&header, 0, sizeof(header)); + memset(&payload, 0, sizeof(payload)); + header.stHdr.dtype = pcmds->cmds[i].dchdr.dtype; + pr_debug("dtype=0x%x\n", header.stHdr.dtype); + if (pcmds->cmds[i].dchdr.dlen <= 2) { + //short write + header.stHdr.ecc = 0x1; + header.stHdr.len[0] = pcmds->cmds[i].payload[0]; + if (pcmds->cmds[i].dchdr.dlen == 2) + header.stHdr.len[1] = pcmds->cmds[i].payload[1]; + pr_debug("%s, line%d, header=0x%4x\n", __func__, __LINE__, header.hdr32); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, header.hdr32); + } else { + //long write + header.stHdr.ecc = 0x5; + header.stHdr.len[0] = pcmds->cmds[i].dchdr.dlen & 0xff; + header.stHdr.len[1] = (pcmds->cmds[i].dchdr.dlen >> 8) & 0xff; + pr_debug("%s, line%d, header=0x%x\n", __func__, __LINE__, header.hdr32); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, header.hdr32); + for (j = 0; j < pcmds->cmds[i].dchdr.dlen; j = j+4) { + memcpy(payload.p, pcmds->cmds[i].payload + j, 4); + pr_debug("payload=0x%x\n", payload.pld32); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, payload.pld32); + } + } + i++; + + //TODO: if the swich cmds count more than GRCP payload limitation (IRIS_REGS), + //it will lost some cmds. + } +} + +void iris_dtg_mode_set(struct msm_fb_data_type *mfd, int tx_mode) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + + iris_dtg_para_set(iris_info.intf_switch_info.rx_current_mode, tx_mode); + + iris_reg_add(IRIS_DTG_ADDR + DTG_DELAY, iris_cfg->dtg_setting.dtg_delay); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL, iris_cfg->dtg_setting.te_ctrl); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL_1, iris_cfg->dtg_setting.te_ctrl_1); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL_2, iris_cfg->dtg_setting.te_ctrl_2); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL_3, iris_cfg->dtg_setting.te_ctrl_3); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL_4, iris_cfg->dtg_setting.te_ctrl_4); + iris_reg_add(IRIS_DTG_ADDR + TE_CTRL_5, iris_cfg->dtg_setting.te_ctrl_5); + iris_reg_add(IRIS_DTG_ADDR + DTG_CTRL_1,iris_cfg->dtg_setting.dtg_ctrl_1); + iris_reg_add(IRIS_DTG_ADDR + DTG_CTRL,iris_cfg->dtg_setting.dtg_ctrl); + iris_reg_add(IRIS_DTG_ADDR + EVS_DLY,iris_cfg->dtg_setting.evs_dly); + iris_reg_add(IRIS_DTG_ADDR + DVS_CTRL, iris_cfg->dtg_setting.dvs_ctrl); + iris_reg_add(IRIS_DTG_ADDR + TE_DLY, iris_cfg->dtg_setting.te_dly); + iris_reg_add(IRIS_DTG_ADDR + TE_DLY_1, iris_cfg->dtg_setting.te_dly_1); + iris_reg_add(IRIS_DTG_ADDR + VFP_CTRL_1, iris_cfg->dtg_setting.vfp_ctrl_1); + iris_reg_add(IRIS_DTG_ADDR + REGSEL, 1); +} + +void iris_mipitx_cmd_to_video_proc(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + uint32_t dport_ctrl0, dsi_tx_ctrl, pwil_ctrl; + + if (iris_cfg->tx_switch_state != IRIS_TX_SWITCH_NONE) + pr_info("%s, dsi mode switch state = %d\n", + __func__, iris_cfg->tx_switch_state); + + switch (iris_cfg->tx_switch_state) { + case IRIS_TX_SWITCH_STEP1: + pwil_ctrl = 0xa0c80004; + + mutex_lock(&mfd->iris_conf.cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + PWIL_CTRL_OFFS, pwil_ctrl); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_STEP2; + break; + case IRIS_TX_SWITCH_STEP2: + dport_ctrl0 = 0xe0e24037; + dsi_tx_ctrl = 0x0a00c039; + + mutex_lock(&mfd->iris_conf.cmd_mutex); + /* set Iris2p output to video */ + iris_reg_add(IRIS_DPORT_ADDR + DPORT_CTRL0_OFFS, dport_ctrl0); + iris_reg_add(IRIS_DPORT_ADDR + DPORT_REGSEL, 1); + iris_reg_add(IRIS_MIPI_TX_ADDR + DSI_TX_CTRL, dsi_tx_ctrl); + iris_reg_add(IRIS_MIPI_TX_ADDR + IRIS_MIPI_ADDR_OFFSET + DSI_TX_CTRL, dsi_tx_ctrl); + iris_dtg_mode_set(mfd, MIPI_VIDEO_MODE); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_STEP3; + break; + case IRIS_TX_SWITCH_STEP3: + mutex_lock(&mfd->iris_conf.cmd_mutex); +#ifdef IRIS2_PANEL + uint32_t count; + /* set Iris2 input to video */ + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x0100bf13); + + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x05004009);//nop + for (count = 0; count < 16; count++) + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00000000); + + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x05001829);//header + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x5057494c);//pwil + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x47524350);//grcp + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00000003); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00020000); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0xf1240004);//addr + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00002098); +#else + iris_mipi_tx_cmds_build(mfd, &(iris_info.tx_switch_cmd.mipitx_cmd2vid_cmds)); +#endif + mutex_unlock(&mfd->iris_conf.cmd_mutex); + iris_info.intf_switch_info.tx_current_mode = MIPI_VIDEO_MODE; + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_NONE; + break; + default: + break; + } + +} + +void iris_mipitx_video_to_cmd_proc(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + uint32_t dport_ctrl0, dsi_tx_ctrl, pwil_ctrl; + + if (iris_cfg->tx_switch_state != IRIS_TX_SWITCH_NONE) + pr_info("%s, dsi mode switch state = %d\n", + __func__, iris_cfg->tx_switch_state); + + switch (iris_cfg->tx_switch_state) { + case IRIS_TX_SWITCH_STEP1: + mutex_lock(&mfd->iris_conf.cmd_mutex); +#ifdef IRIS2_PANEL + uint32_t count; + /* set Iris2 input to cmd */ + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x01007f13); + + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x05004009); + for (count = 0; count < 16; count++) + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00000000); + + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_HEADER_OFFS, 0x05001829); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x5057494c); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x47524350); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00000003); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x00020000); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0xf1240004); + iris_reg_add(IRIS_MIPI_TX_ADDR + WR_PACKET_PAYLOAD_OFFS, 0x0000209a); +#else + iris_mipi_tx_cmds_build(mfd, &(iris_info.tx_switch_cmd.mipitx_vid2cmd_cmds)); +#endif + mutex_unlock(&mfd->iris_conf.cmd_mutex); + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_STEP2; + break; + case IRIS_TX_SWITCH_STEP2: + dport_ctrl0 = 0xe0e2c037; + dsi_tx_ctrl = 0x0a00c139; + + mutex_lock(&mfd->iris_conf.cmd_mutex); + /* set Iris2p_DPORT to output both TTL&PB */ + iris_reg_add(IRIS_DPORT_ADDR + DPORT_CTRL0_OFFS, dport_ctrl0); + iris_reg_add(IRIS_DPORT_ADDR + DPORT_REGSEL, 1); + iris_dtg_mode_set(mfd, MIPI_CMD_MODE); + + /* set Iris2p output to cmd */ + iris_reg_add(IRIS_MIPI_TX_ADDR + DSI_TX_CTRL, dsi_tx_ctrl); + iris_reg_add(IRIS_MIPI_TX_ADDR + IRIS_MIPI_ADDR_OFFSET + DSI_TX_CTRL, dsi_tx_ctrl); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_STEP3; + break; + case IRIS_TX_SWITCH_STEP3: + pwil_ctrl = 0xa0c80014; + dport_ctrl0 = 0xe0e28037; + /* set Iris2p_DPORT to output PB */ + mutex_lock(&mfd->iris_conf.cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + PWIL_CTRL_OFFS, pwil_ctrl); + iris_reg_add(IRIS_DPORT_ADDR + DPORT_CTRL0_OFFS, dport_ctrl0); + iris_reg_add(IRIS_DPORT_ADDR + DPORT_REGSEL, 1); + mutex_unlock(&mfd->iris_conf.cmd_mutex); + + iris_info.intf_switch_info.tx_current_mode = MIPI_CMD_MODE; + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_NONE; + default: + break; + } + +} + +void iris_mipitx_intf_switch_state_reset(struct msm_fb_data_type *mfd, u32 new_mode, bool debug) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + + /* reasonability check */ + if (!iris_info.intf_switch_info.tx_switch_enable) { + pr_err("tx switch is not enabled\n"); + return; + } else if (iris_info.intf_switch_info.tx_current_mode == new_mode) { + pr_err("current mode is %d\n", new_mode); + return; + } else if ((IRIS_RFB_MODE != iris_cfg->current_mode) + && (IRIS_FRC_PRE != iris_cfg->current_mode) + && (IRIS_RFB_PRE != iris_cfg->current_mode)) { + pr_err("iris is not in RFB mode\n"); + return; + } + + iris_cfg->tx_switch_new_mode = new_mode; + iris_cfg->tx_switch_state = IRIS_TX_SWITCH_STEP1; + iris_cfg->tx_switch_debug_flag = debug; +} + +u8 iris_mipitx_interface_switch(struct msm_fb_data_type *mfd, bool debug) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + + if ((IRIS_TX_SWITCH_NONE == iris_cfg->tx_switch_state) || (debug != iris_cfg->tx_switch_debug_flag)) + return 0; + + if (iris_cfg->tx_switch_new_mode == MIPI_CMD_MODE) + iris_mipitx_video_to_cmd_proc(mfd); + else + iris_mipitx_cmd_to_video_proc(mfd); + + return 1; +} + +void iris_dsimode_update(char mode) +{ + pr_info("%s, mode=%d\n", __func__, mode); + iris_info.work_mode.rx_mode = (DSI_VIDEO_MODE == mode) ? MIPI_VIDEO_MODE : MIPI_CMD_MODE; +} + +int iris_mode_frc_prepare_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_frc_prepare_state) + { + case IRIS_FRC_PATH_PROXY: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_memc_path_commands_update(); + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->memc_prepare_work); + eiris_frc_prepare_state = IRIS_FRC_WAIT_PREPARE_DONE; + } + break; + case IRIS_FRC_WAIT_PREPARE_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + eiris_frc_prepare_state = IRIS_FRC_PRE_DONE; + break; + case IRIS_FRC_PRE_DONE: + iris_proc_constant_ratio(iris_cfg); + ret = true; + break; + default: + break; + } + + return ret; + +} + +int iris_mode_rfb2frc_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_mode_rfb2frc_state) + { + case IRIS_RFB_FRC_SWITCH_COMMAND: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_proc_frcc_setting(g_mfd); + iris_memc_reg_write(g_mfd, iris_info.fi_demo_win_info); + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->memc_work); + eiris_mode_rfb2frc_state = IRIS_RFB_FRC_SWITCH_DONE; + } + break; + case IRIS_RFB_FRC_SWITCH_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + ret = true; + } + break; + default: + break; + } + + return ret; + +} + +int iris_mode_rfb_prepare_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_rfb_prepare_state) + { + case IRIS_RFB_PATH_PROXY: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_cfg->iris_ratio_updated = false; + iris_cfg->prev_dvts = 0; + iris_cfg->repeat = IRIS_REPEAT_NO; + eiris_rfb_prepare_state = IRIS_RFB_PRE_DONE; + } + break; + case IRIS_RFB_PRE_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + ret = true; + } + break; + default: + break; + } + + return ret; + +} + +int iris_mode_frc2rfb_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_mode_frc2rfb_state) + { + case IRIS_FRC_RFB_SWITCH_COMMAND: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_rfb_path_commands_update(); + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->rfb_work); + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_SWITCH_DONE; + } + break; + case IRIS_FRC_RFB_SWITCH_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + if (debug_new_repeat == 0) + { + unsigned int reg_cap = 0xc0000003; + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x0218, reg_cap); + mutex_unlock(&iris_cfg->cmd_mutex); + } + iris_cfg->pt_switch = false; + ret = true; + } + break; + default: + break; + } + + return ret; + +} + +int iris_mode_frc_cancel_video(struct msm_fb_data_type *mfd) +{ + int ret = false; + + return ret; +} + +int iris_mode_pt_prepare_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_pt_prepare_state) + { + case IRIS_PT_PATH_PROXY: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_pt_path_commands_update(); + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->pt_prepare_work); + eiris_pt_prepare_state = IRIS_PT_WAIT_PREPARE_DONE; + } + break; + case IRIS_PT_WAIT_PREPARE_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) { + eiris_pt_prepare_state = IRIS_PT_PRE_DONE; + } + break; + case IRIS_PT_PRE_DONE: + ret = true; + break; + default: + break; + } + return ret; +} + +int iris_mode_pt2rfb_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_mode_pt2rfb_state) + { + case IRIS_PT_RFB_SWITCH_COMMAND: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_rfb_path_commands_update(); + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->rfb_work); + eiris_mode_pt2rfb_state = IRIS_PT_RFB_SWITCH_DONE; + } + break; + case IRIS_PT_RFB_SWITCH_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_cfg->pt_switch = false; + ret = true; + } + break; + default: + break; + } + + return ret; + +} + +int iris_mode_rfb2pt_video(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + + switch (eiris_mode_rfb2pt_state) + { + case IRIS_RFB_PT_SWITCH_COMMAND: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + atomic_inc(&iris_cfg->mode_switch_cnt); + schedule_work(&iris_cfg->pt_work); + eiris_mode_rfb2pt_state = IRIS_RFB_PT_SWITCH_DONE; + } + break; + case IRIS_RFB_PT_SWITCH_DONE: + if(atomic_read(&g_mfd->iris_conf.mode_switch_cnt) == 0) + { + iris_cfg->pt_switch = false; + ret = true; + } + break; + default: + break; + } + + return ret; + +} + +int iris_mode_switch_video(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_panel_data *pdata; + struct iris_config *iris_cfg = &mfd->iris_conf; + int pre_current_mode = iris_cfg->current_mode; + + if (!g_dsi_ctrl) { + pdata = mdp5_data->ctl->panel_data; + g_dsi_ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + } + + if (mfd->index != 0) + return -EFAULT; + + if(iris_cfg->sf_mode_change_start) + { + switch(iris_cfg->sf_notify_mode) { + case IRIS_MODE_FRC_PREPARE: + eiris_frc_prepare_state = IRIS_FRC_PATH_PROXY; + break; + case IRIS_MODE_RFB2FRC: + eiris_mode_rfb2frc_state = IRIS_RFB_FRC_SWITCH_COMMAND; + break; + case IRIS_MODE_RFB_PREPARE: + eiris_rfb_prepare_state = IRIS_RFB_PATH_PROXY; + break; + case IRIS_MODE_FRC2RFB: + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_SWITCH_COMMAND; + break; + case IRIS_MODE_PT_PREPARE: + case IRIS_MODE_PTLOW_PREPARE: + eiris_pt_prepare_state = IRIS_PT_PATH_PROXY; + break; + case IRIS_MODE_RFB2PT: + eiris_mode_rfb2pt_state = IRIS_RFB_PT_SWITCH_COMMAND; + break; + case IRIS_MODE_PT2RFB: + eiris_mode_pt2rfb_state = IRIS_PT_RFB_SWITCH_COMMAND; + break; + default: + break; + } + + iris_cfg->sf_mode_change_start = false; + } + + switch(iris_cfg->sf_notify_mode) { + case IRIS_MODE_FRC_PREPARE: + if(iris_cfg->current_mode != IRIS_RFB_MODE) + break; + if(true == iris_mode_frc_prepare_video(mfd)) + { + iris_cfg->current_mode = IRIS_FRC_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_FRC_PREPARE_DONE; + } + else + { + if(eiris_frc_prepare_state == IRIS_FRC_PRE_TIMEOUT) + { + iris_cfg->sf_notify_mode = IRIS_MODE_FRC_PREPARE_TIMEOUT; + } + } + break; + case IRIS_MODE_RFB2FRC: + if(iris_cfg->current_mode != IRIS_FRC_PRE) + break; + if(true == iris_mode_rfb2frc_video(mfd)) + { + iris_cfg->current_mode = IRIS_FRC_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_FRC; + } + break; + case IRIS_MODE_FRC_CANCEL: + if((iris_cfg->current_mode == IRIS_FRC_PRE) || (eiris_frc_prepare_state == IRIS_FRC_PRE_TIMEOUT)) + { + iris_mode_frc_cancel_video(mfd); + iris_cfg->current_mode = IRIS_RFB_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + eiris_frc_prepare_state = IRIS_FRC_PATH_PROXY; + } + break; + case IRIS_MODE_RFB_PREPARE: + if((iris_cfg->current_mode == IRIS_FRC_MODE) || (iris_cfg->current_mode == IRIS_PT_MODE)) + { + if(true == iris_mode_rfb_prepare_video(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB_PREPARE_DONE; + } + } + break; + case IRIS_MODE_FRC2RFB: + if(iris_cfg->current_mode != IRIS_RFB_PRE) + break; + if(true == iris_mode_frc2rfb_video(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + } + break; + case IRIS_MODE_PT_PREPARE: + case IRIS_MODE_PTLOW_PREPARE: + if(iris_cfg->current_mode != IRIS_RFB_MODE) + break; + if(true == iris_mode_pt_prepare_video(mfd)) + { + iris_cfg->pt_switch = true; + iris_cfg->current_mode = IRIS_PT_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT_PREPARE_DONE; + } + break; + case IRIS_MODE_RFB2PT: + if(iris_cfg->current_mode != IRIS_PT_PRE) + break; + if(true == iris_mode_rfb2pt_video(mfd)) + { + iris_cfg->current_mode = IRIS_PT_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT; + } + break; + case IRIS_MODE_PT2RFB: + if(iris_cfg->current_mode != IRIS_RFB_PRE) + break; + if(true == iris_mode_pt2rfb_video(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + } + break; + case IRIS_MODE_PT2BYPASS: + if(iris_cfg->current_mode != IRIS_PT_MODE) + break; + if (true == iris_mode_bypass_switch(IRIS_MODE_PT2BYPASS)) { + iris_cfg->current_mode = IRIS_BYPASS_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_BYPASS; + } + break; + case IRIS_MODE_BYPASS2PT: + if(iris_cfg->current_mode != IRIS_BYPASS_MODE) + break; + if (true == iris_mode_bypass_switch(IRIS_MODE_BYPASS2PT)) { + iris_cfg->current_mode = IRIS_PT_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT; + } + break; + + default: + break; + } + + if(pre_current_mode != iris_cfg->current_mode) + pr_info("%s, %d: mode from %d to %d\n", __func__, __LINE__, pre_current_mode, iris_cfg->current_mode); + + return 0; +} + +int iris_mode_frc_prepare(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + u32 val = 0; + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_frc_prepare_state) + { + case IRIS_FRC_PRE_TX_SWITCH: + if (iris_mipitx_interface_switch(mfd, 0)) + break; + else + eiris_frc_prepare_state = IRIS_FRC_PATH_PROXY; + break; + case IRIS_FRC_PATH_PROXY: +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + // drc LP MEMC update, don't take effect now + if (g_mfd->iris_conf.drc_enable) + iris_Drc_LPMemc_update(mfd); +#endif + iris_memc_path_commands_update(); + if (debug_mode_switch_enabled) { + memcpy(&cmd_rfb, memc_data_path_config, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + iris_cfg->kickoff_cnt = 1; + eiris_frc_prepare_state = IRIS_FRC_WAIT_PREPARE_DONE; + break; + case IRIS_FRC_WAIT_PREPARE_DONE: + if (iris_cfg->kickoff_cnt++ < 30) + { + if (debug_mode_switch_enabled) + { + #if defined(FPGA_PLATFORM) + if (iris_cfg->kickoff_cnt == 25) + val = IRIS_FRC_PRE; + else val = 0; + #else + val = iris_pi_read(g_dsi_ctrl, IRIS_MODE_ADDR); + if(val == 2) + val = IRIS_FRC_PRE; + #endif + } + else + { + val = IRIS_FRC_PRE; + } + + if (val != IRIS_FRC_PRE) { + pr_debug("iris: mode = %08x, cnt = %d\n", val, iris_cfg->kickoff_cnt); + } + else + { + eiris_frc_prepare_state = IRIS_FRC_PRE_DONE; + } + } + else + { + pr_debug("iris: memc prep time out\n"); + eiris_frc_prepare_state = IRIS_FRC_PRE_TIMEOUT; + } + break; + case IRIS_FRC_PRE_DONE: + frc_repeat_enter = false; + iris_cfg->cap_enable = true; + iris_cfg->cap_change = false; + iris_proc_frcc_setting(g_mfd); + iris_memc_reg_write(g_mfd, iris_info.fi_demo_win_info); + iris_mcuclk_divider_change(g_dsi_ctrl, 0); + iris_proc_constant_ratio(iris_cfg); + ret = true; + break; + case IRIS_FRC_PRE_TIMEOUT: + break; + default: + break; + } + + return ret; + +} + +static int iris_mode_rfb2frc(struct msm_fb_data_type *mfd) +{ + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_mode_rfb2frc_state) + { + case IRIS_RFB_FRC_SWITCH_COMMAND: + if (debug_mode_switch_enabled) { + memcpy(&cmd_rfb, memc_mode_enter, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + eiris_mode_rfb2frc_state = IRIS_RFB_FRC_SWITCH_DONE; + break; + case IRIS_RFB_FRC_SWITCH_DONE: + ret = true; + break; + default: + break; + } + + return ret; +} + +static int iris_mode_rfb_prepare(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + int vper; + + switch (eiris_rfb_prepare_state) + { + case IRIS_RFB_PATH_PROXY: + if (iris_cfg->pt_switch) + eiris_rfb_prepare_state = IRIS_RFB_PRE_DONE; + iris_cfg->iris_ratio_updated = false; + iris_cfg->prev_dvts = 0; + iris_cfg->repeat = IRIS_REPEAT_NO; + iris_cfg->true_cut_enable = false; + iris_cfg->input_vfr = 0; +#if defined(CONFIG_IRIS2P_DRC_SUPPORT) + //drc exit + if (g_mfd->iris_conf.drc_enable) + iris_calc_drc_exit(mfd); +#endif + vper = iris_get_vtotal(&iris_info.output_timing); + if (iris_is_cmdin_videout() && iris_cfg->sw_te_period != vper) { + iris_cfg->sw_te_period = vper; + iris_set_te(iris_cfg, true); + } + + eiris_rfb_prepare_state = IRIS_RFB_WAIT_PREPARE_DONE; + break; + case IRIS_RFB_WAIT_PREPARE_DONE: + eiris_rfb_prepare_state = IRIS_RFB_PRE_DONE; + break; + case IRIS_RFB_PRE_DONE: + ret = true; + break; + default: + break; + } + + return ret; +} + +static int iris_mode_frc2rfb(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_mode_frc2rfb_state) + { + case IRIS_FRC_RFB_SWITCH_COMMAND: + pt_enable[0] = 0x3 << 2; + pt_enable[1] = 0x1; + memcpy(&cmd_rfb, pt_mode_enter, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_DATA_PATH; + iris_cfg->kickoff_cnt = 0; + break; + case IRIS_FRC_RFB_DATA_PATH: + iris_cfg->kickoff_cnt++; + if (iris_cfg->kickoff_cnt <=2) + break; + iris_rfb_path_commands_update(); + if (debug_mode_switch_enabled) { + memcpy(&cmd_rfb, rfb_data_path_config, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_SWITCH_DONE; + iris_cfg->kickoff_cnt = 0; + break; + case IRIS_FRC_RFB_SWITCH_DONE: + iris_cfg->kickoff_cnt++; + if (iris_cfg->kickoff_cnt <=6) + break; + if (debug_new_repeat == 0) + { + unsigned int reg_cap = 0xc0000003; + mutex_lock(&iris_cfg->cmd_mutex); + iris_reg_add(IRIS_PWIL_ADDR + 0x0218, reg_cap); + mutex_unlock(&iris_cfg->cmd_mutex); + } + iris_mcuclk_divider_change(g_dsi_ctrl, 1); + iris_cfg->pt_switch = false; + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_TX_SWITCH; + break; + case IRIS_FRC_RFB_TX_SWITCH: + if (iris_mipitx_interface_switch(mfd, 0)) + break; + else + ret = true; + break; + default: + break; + } + + return ret; + +} + +static int iris_mode_frc_cancel(struct msm_fb_data_type *mfd) +{ + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + + switch (eiris_frc_cancel_state) + { + case IRIS_FRC_CANCEL_PATH_PROXY: + iris_rfb_enter_cmds[21] = (iris_rfb_enter_cmds[21] & 0xFC) | iris_info.setting_info.dbc_mode; + if (debug_mode_switch_enabled) + { + memcpy(&cmd_rfb, rfb_data_path_config, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + eiris_frc_cancel_state = IRIS_FRC_CANCEL_TX_SWITCH; + break; + case IRIS_FRC_CANCEL_TX_SWITCH: + if (iris_mipitx_interface_switch(mfd, 0)) + break; + else + ret = true; + eiris_frc_cancel_state = IRIS_RFB_PRE_DONE; + break; + case IRIS_FRC_CANCEL_DONE: + ret = true; + break; + default: + break; + } + + return ret; +} + +static int iris_mode_pt2rfb(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_mode_pt2rfb_state) + { + case IRIS_PT_RFB_SWITCH_COMMAND: + pt_enable[0] = 0x3 << 2; + pt_enable[1] = 0x1; + memcpy(&cmd_rfb, pt_mode_enter, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + eiris_mode_pt2rfb_state = IRIS_PT_RFB_DATA_PATH; + break; + case IRIS_PT_RFB_DATA_PATH: + iris_rfb_path_commands_update(); + if (debug_mode_switch_enabled) { + memcpy(&cmd_rfb, rfb_data_path_config, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + + eiris_mode_pt2rfb_state = IRIS_PT_RFB_SWITCH_DONE; + break; + case IRIS_PT_RFB_SWITCH_DONE: + iris_cfg->pt_switch = false; + ret = true; + break; + default: + break; + } + + return ret; + +} + +static int iris_mode_pt_prepare(struct msm_fb_data_type *mfd) +{ + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_pt_prepare_state) + { + case IRIS_PT_PATH_PROXY: + iris_pt_path_commands_update(); + if (debug_mode_switch_enabled) { + memcpy(&cmd_rfb, pt_data_path_config, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + } + eiris_pt_prepare_state = IRIS_PT_WAIT_PREPARE_DONE; + break; + case IRIS_PT_WAIT_PREPARE_DONE: + eiris_pt_prepare_state = IRIS_PT_PRE_DONE; + break; + case IRIS_PT_PRE_DONE: + ret = true; + break; + default: + break; + } + + return ret; +} + + +static int iris_mode_rfb2pt(struct msm_fb_data_type *mfd) +{ + struct iris_config *iris_cfg = &mfd->iris_conf; + int ret = false; + struct dcs_cmd_req cmdreq; + struct dsi_cmd_desc cmd_rfb; + + cmdreq.cmds = &cmd_rfb; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL | CMD_REQ_HS_MODE; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + cmdreq.cmds_cnt = 1; + + switch (eiris_mode_rfb2pt_state) + { + case IRIS_RFB_PT_SWITCH_COMMAND: + pt_enable[0] = 0x0 << 2; + pt_enable[1] = 0x1; + memcpy(&cmd_rfb, pt_mode_enter, sizeof(struct dsi_cmd_desc)); + mdss_dsi_cmdlist_put(g_dsi_ctrl, &cmdreq); + eiris_mode_rfb2pt_state = IRIS_RFB_PT_DATA_PATH; + break; + case IRIS_RFB_PT_DATA_PATH: + eiris_mode_rfb2pt_state = IRIS_RFB_PT_SWITCH_DONE; + break; + case IRIS_RFB_PT_SWITCH_DONE: + iris_cfg->pt_switch = false; + ret = true; + break; + default: + break; + } + + return ret; + +} + +static int iris_mode_pt_bypass_switch(struct dsi_cmd_desc *desc) +{ + struct dcs_cmd_req req = { + .cmds = desc, + .cmds_cnt = 1, + .flags = CMD_REQ_COMMIT | CMD_REQ_NO_MAX_PKT_SIZE | CMD_CLK_CTRL | CMD_REQ_HS_MODE, + .rlen = 0, + .rbuf = NULL, + .cb = NULL + }; + + mdss_dsi_cmdlist_put(g_dsi_ctrl, &req); + + return 0; +} + +static int iris_mode_bypass2pt(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + struct dsi_cmd_desc desc_list[] = { + { { DTYPE_GEN_WRITE1, 1, 0, 0, 0, sizeof(bypass_enable) }, &bypass_enable}, + }; + + if ((MIPI_VIDEO_MODE == pwork_mode->rx_mode) && + (pwork_mode->rx_mode == pwork_mode->tx_mode)) { + bypass_enable = 0xbf; + } else if ((MIPI_CMD_MODE == pwork_mode->rx_mode) && + (pwork_mode->rx_mode == pwork_mode->tx_mode)) { + bypass_enable = 0x7f; + } + pr_info("bypass_enable: %x, rx_mode: %d, tx_mode: %d\n", bypass_enable, pwork_mode->rx_mode, pwork_mode->tx_mode); + iris_mode_pt_bypass_switch(desc_list); + return true; +} + +static int iris_mode_pt2bypass(void) +{ + struct iris_work_mode *pwork_mode = &(iris_info.work_mode); + struct dsi_cmd_desc desc_list[] = { + { { DTYPE_GEN_WRITE1, 1, 0, 0, 0, sizeof(bypass_enable) }, &bypass_enable}, + }; + + if ((MIPI_VIDEO_MODE == pwork_mode->rx_mode) && + (pwork_mode->rx_mode == pwork_mode->tx_mode)) { + bypass_enable = 0xff; + } else if ((MIPI_CMD_MODE == pwork_mode->rx_mode) && + (pwork_mode->rx_mode == pwork_mode->tx_mode)) { + bypass_enable = 0xdf; + } + pr_info("bypass_enable: %x, rx_mode: %d, tx_mode: %d\n", bypass_enable, pwork_mode->rx_mode, pwork_mode->tx_mode); + iris_mode_pt_bypass_switch(desc_list); + return true; +} + +static int iris_mode_bypass_switch(int val) +{ + int type = iris_info.abypss_ctrl.analog_bypass_enable; + int ret = false; + + if (type == false) { + if (IRIS_MODE_PT2BYPASS == val){ + ret = iris_mode_pt2bypass(); + } else if (IRIS_MODE_BYPASS2PT == val) { + ret = iris_mode_bypass2pt(); + } + } else { + if (IRIS_MODE_PT2BYPASS == val) { + ret = iris_pt_to_abypass_switch(g_dsi_ctrl); + } else if (IRIS_MODE_BYPASS2PT == val){ + ret = iris_abypass_to_pt_switch(g_dsi_ctrl); + } + } + return ret; +} + +int iris_mode_switch_cmd(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_panel_data *pdata; + struct iris_config *iris_cfg = &mfd->iris_conf; + int pre_current_mode = iris_cfg->current_mode; + + if (mfd->index != 0) + return -EFAULT; + + if (!g_dsi_ctrl) { + pdata = mdp5_data->ctl->panel_data; + g_dsi_ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); + } + + if(iris_cfg->sf_mode_change_start) + { + switch(iris_cfg->sf_notify_mode) { + case IRIS_MODE_FRC_PREPARE: + eiris_frc_prepare_state = IRIS_FRC_PRE_TX_SWITCH; + iris_mipitx_intf_switch_state_reset(mfd, MIPI_VIDEO_MODE, 0); + break; + case IRIS_MODE_RFB2FRC: + eiris_mode_rfb2frc_state = IRIS_RFB_FRC_SWITCH_COMMAND; + break; + case IRIS_MODE_RFB_PREPARE: + eiris_rfb_prepare_state = IRIS_RFB_PATH_PROXY; + break; + case IRIS_MODE_FRC2RFB: + eiris_mode_frc2rfb_state = IRIS_FRC_RFB_SWITCH_COMMAND; + iris_mipitx_intf_switch_state_reset(mfd, MIPI_CMD_MODE, 0); + break; + case IRIS_MODE_FRC_CANCEL: + eiris_frc_cancel_state = IRIS_FRC_CANCEL_PATH_PROXY; + iris_mipitx_intf_switch_state_reset(mfd, MIPI_CMD_MODE, 0); + break; + case IRIS_MODE_PT_PREPARE: + case IRIS_MODE_PTLOW_PREPARE: + eiris_pt_prepare_state = IRIS_PT_PATH_PROXY; + break; + case IRIS_MODE_RFB2PT: + eiris_mode_rfb2pt_state = IRIS_RFB_PT_SWITCH_COMMAND; + break; + case IRIS_MODE_PT2RFB: + eiris_mode_pt2rfb_state = IRIS_PT_RFB_SWITCH_COMMAND; + break; + case IRIS_MODE_PT2BYPASS: + iris_abypass_switch_state_init(IRIS_MODE_PT2BYPASS); + break; + case IRIS_MODE_BYPASS2PT: + iris_abypass_switch_state_init(IRIS_MODE_BYPASS2PT); + break; + default: + break; + } + + iris_cfg->sf_mode_change_start = false; + } + + switch(iris_cfg->sf_notify_mode) { + case IRIS_MODE_FRC_PREPARE: + if(iris_cfg->current_mode != IRIS_RFB_MODE) + break; + if(true == iris_mode_frc_prepare(mfd)) + { + iris_cfg->current_mode = IRIS_FRC_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_FRC_PREPARE_DONE; + } + else + { + if(eiris_frc_prepare_state == IRIS_FRC_PRE_TIMEOUT) + { + iris_cfg->sf_notify_mode = IRIS_MODE_FRC_PREPARE_TIMEOUT; + } + } + break; + case IRIS_MODE_RFB2FRC: + if(iris_cfg->current_mode != IRIS_FRC_PRE) + break; + if(true == iris_mode_rfb2frc(mfd)) + { + iris_cfg->current_mode = IRIS_FRC_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_FRC; + } + break; + case IRIS_MODE_FRC_CANCEL: + if((iris_cfg->current_mode == IRIS_FRC_PRE) || (eiris_frc_prepare_state == IRIS_FRC_PRE_TIMEOUT)) + { + if (true == iris_mode_frc_cancel(mfd)) { + iris_cfg->current_mode = IRIS_RFB_MODE; + eiris_frc_prepare_state = IRIS_FRC_PRE_TX_SWITCH; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + } + } + break; + case IRIS_MODE_RFB_PREPARE: + if((iris_cfg->current_mode == IRIS_FRC_MODE) || (iris_cfg->current_mode == IRIS_PT_MODE)) + { + if(true == iris_mode_rfb_prepare(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB_PREPARE_DONE; + } + } + break; + case IRIS_MODE_FRC2RFB: + if(iris_cfg->current_mode != IRIS_RFB_PRE) + break; + if(true == iris_mode_frc2rfb(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + } + break; + case IRIS_MODE_PT_PREPARE: + case IRIS_MODE_PTLOW_PREPARE: + if(iris_cfg->current_mode != IRIS_RFB_MODE) + break; + if(true == iris_mode_pt_prepare(mfd)) + { + iris_cfg->pt_switch = true; + iris_cfg->current_mode = IRIS_PT_PRE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT_PREPARE_DONE; + } + break; + case IRIS_MODE_RFB2PT: + if(iris_cfg->current_mode != IRIS_PT_PRE) + break; + if(true == iris_mode_rfb2pt(mfd)) + { + iris_cfg->current_mode = IRIS_PT_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT; + } + break; + case IRIS_MODE_PT2RFB: + if(iris_cfg->current_mode != IRIS_RFB_PRE) + break; + if(true == iris_mode_pt2rfb(mfd)) + { + iris_cfg->current_mode = IRIS_RFB_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + } + break; + case IRIS_MODE_PT2BYPASS: + if(iris_cfg->current_mode != IRIS_PT_MODE) + break; + if (true == iris_mode_bypass_switch(IRIS_MODE_PT2BYPASS)) { + iris_cfg->current_mode = IRIS_BYPASS_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_BYPASS; + } + break; + case IRIS_MODE_BYPASS2PT: + if(iris_cfg->current_mode != IRIS_BYPASS_MODE) + break; + if (true == iris_mode_bypass_switch(IRIS_MODE_BYPASS2PT)) { + iris_cfg->current_mode = IRIS_PT_MODE; + iris_cfg->sf_notify_mode = IRIS_MODE_PT; + } + break; + default: + break; + } + + if(pre_current_mode != iris_cfg->current_mode) + pr_info("%s, %d: mode from %d to %d\n", __func__, __LINE__, pre_current_mode, iris_cfg->current_mode); + + return 0; + +} + +void iris_mode_switch_reset(struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct iris_config *iris_cfg = &g_mfd->iris_conf; + iris_cfg->sf_notify_mode = IRIS_MODE_RFB; + iris_cfg->kickoff_cnt = 0; + iris_cfg->sf_mode_change_start = false; + iris_cfg->iris_ratio_updated = false; + iris_cfg->repeat = IRIS_REPEAT_NO; + iris_cfg->pt_switch = false; + iris_cfg->true_cut_enable = false; + iris_cfg->current_mode = IRIS_RFB_MODE; +} + diff --git a/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.h b/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.h new file mode 100644 index 000000000000..a29f557b2076 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_dsi_iris2p_mode_switch.h @@ -0,0 +1,10 @@ +#ifndef MDSS_DSI_IRIS_MODE_SWITCH +#define MDSS_DSI_IRIS_MODE_SWITCH +void iris_dsimode_update(char mode); +void iris_mipitx_intf_switch_state_reset(struct msm_fb_data_type *mfd, u32 new_mode, bool debug); +u8 iris_mipitx_interface_switch(struct msm_fb_data_type *mfd, bool debug); +void iris_dms_config(struct mdss_dsi_ctrl_pdata *ctrl); +int iris_mode_switch_cmd(struct msm_fb_data_type *mfd); +int iris_mode_switch_video(struct msm_fb_data_type *mfd); +void iris_mode_switch_reset(struct mdss_dsi_ctrl_pdata *ctrl); +#endif diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index dbd58f93e907..d66da235ea80 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -10,6 +10,9 @@ * GNU General Public License for more details. */ + + + #include #include #include @@ -22,898 +25,1340 @@ #include #include #include - + #include "mdss_dsi.h" #include "mdss_dba_utils.h" #include "mdss_debug.h" - + +#include +#include +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) +#include "mdss_dsi_iris2p_lightup.h" +#endif + #define DT_CMD_HDR 6 #define DEFAULT_MDP_TRANSFER_TIME 14000 - + #define VSYNC_DELAY msecs_to_jiffies(17) - -DEFINE_LED_TRIGGER(bl_led_trigger); - -void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl) -{ - if (ctrl->pwm_pmi) - return; - - ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); - if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) { - pr_err("%s: Error: lpg_chan=%d pwm request failed", - __func__, ctrl->pwm_lpg_chan); - } - ctrl->pwm_enabled = 0; -} - -bool mdss_dsi_panel_pwm_enable(struct mdss_dsi_ctrl_pdata *ctrl) -{ - bool status = true; - if (!ctrl->pwm_enabled) - goto end; - - if (pwm_enable(ctrl->pwm_bl)) { - pr_err("%s: pwm_enable() failed\n", __func__); - status = false; - } - - ctrl->pwm_enabled = 1; - -end: - return status; -} - -static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) -{ - int ret; - u32 duty; - u32 period_ns; - - if (ctrl->pwm_bl == NULL) { - pr_err("%s: no PWM\n", __func__); - return; - } - - if (level == 0) { - if (ctrl->pwm_enabled) { - ret = pwm_config_us(ctrl->pwm_bl, level, - ctrl->pwm_period); - if (ret) - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - pwm_disable(ctrl->pwm_bl); - } - ctrl->pwm_enabled = 0; - return; - } - - duty = level * ctrl->pwm_period; - duty /= ctrl->bklt_max; - - pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n", - __func__, ctrl->bklt_ctrl, ctrl->pwm_period, - ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan); - - pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__, - ctrl->ndx, level, duty); - - if (ctrl->pwm_period >= USEC_PER_SEC) { - ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); - if (ret) { - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - return; - } - } else { - period_ns = ctrl->pwm_period * NSEC_PER_USEC; - ret = pwm_config(ctrl->pwm_bl, - level * period_ns / ctrl->bklt_max, - period_ns); - if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", - __func__, ret); - return; - } - } - - if (!ctrl->pwm_enabled) { - ret = pwm_enable(ctrl->pwm_bl); - if (ret) - pr_err("%s: pwm_enable() failed err=%d\n", __func__, - ret); - ctrl->pwm_enabled = 1; - } -} - -static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */ -static struct dsi_cmd_desc dcs_read_cmd = { - {DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)}, - dcs_cmd -}; - -int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, - char cmd1, void (*fxn)(int), char *rbuf, int len) -{ - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return -EINVAL; - } - - dcs_cmd[0] = cmd0; - dcs_cmd[1] = cmd1; - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = &dcs_read_cmd; - cmdreq.cmds_cnt = 1; - cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; - cmdreq.rlen = len; - cmdreq.rbuf = rbuf; - cmdreq.cb = fxn; /* call back */ - /* - * blocked here, until call back called - */ - - return mdss_dsi_cmdlist_put(ctrl, &cmdreq); -} - -static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, - struct dsi_panel_cmds *pcmds) -{ - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) - return; - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = pcmds->cmds; - cmdreq.cmds_cnt = pcmds->cmd_cnt; - cmdreq.flags = CMD_REQ_COMMIT; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - mdss_dsi_cmdlist_put(ctrl, &cmdreq); -} + + DEFINE_LED_TRIGGER(bl_led_trigger); + + void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl) + { + if (ctrl->pwm_pmi) + return; + + ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); + if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) { + pr_err("%s: Error: lpg_chan=%d pwm request failed", + __func__, ctrl->pwm_lpg_chan); + } + ctrl->pwm_enabled = 0; + } + + bool mdss_dsi_panel_pwm_enable(struct mdss_dsi_ctrl_pdata *ctrl) + { + bool status = true; + if (!ctrl->pwm_enabled) + goto end; + + if (pwm_enable(ctrl->pwm_bl)) { + pr_err("%s: pwm_enable() failed\n", __func__); + status = false; + } + + ctrl->pwm_enabled = 1; + + end: + return status; + } + + static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) + { + int ret; + u32 duty; + u32 period_ns; + + if (ctrl->pwm_bl == NULL) { + pr_err("%s: no PWM\n", __func__); + return; + } + + if (level == 0) { + if (ctrl->pwm_enabled) { + ret = pwm_config_us(ctrl->pwm_bl, level, + ctrl->pwm_period); + if (ret) + pr_err("%s: pwm_config_us() failed err=%d.\n", + __func__, ret); + pwm_disable(ctrl->pwm_bl); + } + ctrl->pwm_enabled = 0; + return; + } + + duty = level * ctrl->pwm_period; + duty /= ctrl->bklt_max; + + pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n", + __func__, ctrl->bklt_ctrl, ctrl->pwm_period, + ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan); + + pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__, + ctrl->ndx, level, duty); + + if (ctrl->pwm_period >= USEC_PER_SEC) { + ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); + if (ret) { + pr_err("%s: pwm_config_us() failed err=%d.\n", + __func__, ret); + return; + } + } else { + period_ns = ctrl->pwm_period * NSEC_PER_USEC; + ret = pwm_config(ctrl->pwm_bl, + level * period_ns / ctrl->bklt_max, + period_ns); + if (ret) { + pr_err("%s: pwm_config() failed err=%d.\n", + __func__, ret); + return; + } + } + + if (!ctrl->pwm_enabled) { + ret = pwm_enable(ctrl->pwm_bl); + if (ret) + pr_err("%s: pwm_enable() failed err=%d\n", __func__, + ret); + ctrl->pwm_enabled = 1; + } + } + + static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */ + static struct dsi_cmd_desc dcs_read_cmd = { + {DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)}, + dcs_cmd + }; + + int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, + char cmd1, void (*fxn)(int), char *rbuf, int len) + { + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return -EINVAL; + } + + dcs_cmd[0] = cmd0; + dcs_cmd[1] = cmd1; + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = &dcs_read_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + cmdreq.rlen = len; + cmdreq.rbuf = rbuf; + cmdreq.cb = fxn; /* call back */ + /* + * blocked here, until call back called + */ + + return mdss_dsi_cmdlist_put(ctrl, &cmdreq); + } + + static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) + { + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) + return; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + } + + + static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds, u32 flags) + { + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = flags; + + /*Panel ON/Off commands should be sent in DSI Low Power Mode*/ + if (pcmds->link_state == DSI_LP_MODE) + cmdreq.flags |= CMD_REQ_LP_MODE; + else if (pcmds->link_state == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + } + + + + + + static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ + static struct dsi_cmd_desc backlight_cmd = { + {DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)}, + led_pwm1 + }; + //samsung s6e3fa6 panel backlight + static char led_pwm2[3] = {0x51, 0x00, 0x00}; /* DTYPE_DCS_LWRITE */ + static struct dsi_cmd_desc backlight_cmd2 = { + {DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm2)}, + led_pwm2 + }; + + static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) + { + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + + pr_debug("%s: level=%d\n", __func__, level); + + + if (ctrl->bklt_max > 255){ + u8 ldata = 0; + u8 hdata = 0; + + if (ctrl->bl_high2bit){ + ldata = level & 0x0ff; + hdata = (level >> 8) & 0x03; + led_pwm2[2] = ldata; + led_pwm2[1] = hdata; + } else{ + ldata = level & 0x03; + hdata = (level >> 2) & 0x0ff; + led_pwm2[2] = ldata; + led_pwm2[1] = hdata; + } + }else + led_pwm1[1] = (unsigned char)level; -static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, - struct dsi_panel_cmds *pcmds, u32 flags) -{ - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return; - } - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = pcmds->cmds; - cmdreq.cmds_cnt = pcmds->cmd_cnt; - cmdreq.flags = flags; - - /*Panel ON/Off commands should be sent in DSI Low Power Mode*/ - if (pcmds->link_state == DSI_LP_MODE) - cmdreq.flags |= CMD_REQ_LP_MODE; - else if (pcmds->link_state == DSI_HS_MODE) - cmdreq.flags |= CMD_REQ_HS_MODE; - - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - mdss_dsi_cmdlist_put(ctrl, &cmdreq); -} - -static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ -static struct dsi_cmd_desc backlight_cmd = { - {DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)}, - led_pwm1 -}; - -static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) -{ - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return; - } - - pr_debug("%s: level=%d\n", __func__, level); - - led_pwm1[1] = (unsigned char)level; - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = &backlight_cmd; - cmdreq.cmds_cnt = 1; - cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - if (ctrl->bklt_dcs_op_mode == DSI_HS_MODE) - cmdreq.flags |= CMD_REQ_HS_MODE; - else - cmdreq.flags |= CMD_REQ_LP_MODE; - - mdss_dsi_cmdlist_put(ctrl, &cmdreq); -} - -static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - int rc = 0; - - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - rc = gpio_request(ctrl_pdata->disp_en_gpio, - "disp_enable"); - if (rc) { - pr_err("request disp_en gpio failed, rc=%d\n", - rc); - goto disp_en_gpio_err; - } - } - rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); - if (rc) { - pr_err("request reset gpio failed, rc=%d\n", - rc); - goto rst_gpio_err; - } - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - rc = gpio_request(ctrl_pdata->avdd_en_gpio, - "avdd_enable"); - if (rc) { - pr_err("request avdd_en gpio failed, rc=%d\n", - rc); - goto avdd_en_gpio_err; - } - } - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); - if (rc) { - pr_err("request dsc/dual mode gpio failed,rc=%d\n", - rc); - goto lcd_mode_sel_gpio_err; - } - } - - return rc; - -lcd_mode_sel_gpio_err: - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) - gpio_free(ctrl_pdata->avdd_en_gpio); -avdd_en_gpio_err: - gpio_free(ctrl_pdata->rst_gpio); -rst_gpio_err: - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) - gpio_free(ctrl_pdata->disp_en_gpio); -disp_en_gpio_err: - return rc; -} - -int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) -{ - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - int rc = 0, val = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - if (ctrl_pdata == NULL) { - pr_err("%s: Invalid ctrl data\n", __func__); - return -EINVAL; - } - - /* if gpio is not valid */ - if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) - return rc; - - pr_debug("%s: enable = %d\n", __func__, enable); - - /* - * if gpio state is false and enable (bl level) is - * non zero then toggle the gpio - */ - if (!ctrl_pdata->bklt_en_gpio_state && enable) { - rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); - if (rc) { - pr_err("request bklt gpio failed, rc=%d\n", rc); - goto free; - } - - if (ctrl_pdata->bklt_en_gpio_invert) - val = 0; + memset(&cmdreq, 0, sizeof(cmdreq)); + if (ctrl->bklt_max > 255){ + cmdreq.cmds = &backlight_cmd2; + }else + cmdreq.cmds = &backlight_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + } + + + static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + int rc = 0; + + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + rc = gpio_request(ctrl_pdata->disp_en_gpio, + "disp_enable"); + if (rc) { + pr_err("request disp_en gpio failed, rc=%d\n", + rc); + goto disp_en_gpio_err; + } + } + rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); + if (rc) { + pr_err("request reset gpio failed, rc=%d\n", + rc); + goto rst_gpio_err; + } + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + rc = gpio_request(ctrl_pdata->avdd_en_gpio, + "avdd_enable"); + if (rc) { + pr_err("request avdd_en gpio failed, rc=%d\n", + rc); + goto avdd_en_gpio_err; + } + } + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); + if (rc) { + pr_err("request dsc/dual mode gpio failed,rc=%d\n", + rc); + goto lcd_mode_sel_gpio_err; + } + } + + return rc; + + lcd_mode_sel_gpio_err: + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + gpio_free(ctrl_pdata->avdd_en_gpio); + avdd_en_gpio_err: + gpio_free(ctrl_pdata->rst_gpio); + rst_gpio_err: + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) + gpio_free(ctrl_pdata->disp_en_gpio); + disp_en_gpio_err: + return rc; + } + + int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0, val = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid ctrl data\n", __func__); + return -EINVAL; + } + + /* if gpio is not valid */ + if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) + return rc; + + pr_debug("%s: enable = %d\n", __func__, enable); + + /* + * if gpio state is false and enable (bl level) is + * non zero then toggle the gpio + */ + if (!ctrl_pdata->bklt_en_gpio_state && enable) { + rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); + if (rc) { + pr_err("request bklt gpio failed, rc=%d\n", rc); + goto free; + } + + if (ctrl_pdata->bklt_en_gpio_invert) + val = 0; + else + val = 1; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) { + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + ctrl_pdata->bklt_en_gpio_state = true; + goto ret; + } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { + /* + * if gpio state is true and enable (bl level) is + * zero then toggle the gpio + */ + if (ctrl_pdata->bklt_en_gpio_invert) + val = 1; + else + val = 0; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + + /* gpio state is true and bl level is non zero */ + goto ret; + + free: + pr_debug("%s: free bklt gpio\n", __func__); + ctrl_pdata->bklt_en_gpio_state = false; + gpio_free(ctrl_pdata->bklt_en_gpio); + ret: + return rc; + } + + int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int i, rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pinfo = &(ctrl_pdata->panel_data.panel_info); + if ((mdss_dsi_is_right_ctrl(ctrl_pdata) && + mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) || + pinfo->is_dba_panel) { + pr_debug("%s:%d, right ctrl gpio configuration not needed\n", + __func__, __LINE__); + return rc; + } + + if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + pr_debug("%s:%d, reset line not configured\n", + __func__, __LINE__); + } + + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { + pr_debug("%s:%d, reset line not configured\n", + __func__, __LINE__); + return rc; + } + + pr_debug("%s: enable = %d\n", __func__, enable); + + if (enable) { + rc = mdss_dsi_request_gpios(ctrl_pdata); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + if (!pinfo->cont_splash_enabled) { + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + rc = gpio_direction_output( + ctrl_pdata->disp_en_gpio, 1); + if (rc) { + pr_err("%s: unable to set dir for en gpio\n", + __func__); + goto exit; + } + } + + if (pdata->panel_info.rst_seq_len) { + rc = gpio_direction_output(ctrl_pdata->rst_gpio, + pdata->panel_info.rst_seq[0]); + if (rc) { + pr_err("%s: unable to set dir for rst gpio\n", + __func__); + goto exit; + } + } + + for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { + gpio_set_value((ctrl_pdata->rst_gpio), + pdata->panel_info.rst_seq[i]); + if (pdata->panel_info.rst_seq[++i]) + usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); + } + + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 0); + } else { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 1); + } + if (rc) { + pr_err("%s: unable to set dir for avdd_en gpio\n", + __func__); + goto exit; + } + } + } + + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + bool out = false; + + if ((pinfo->mode_sel_state == MODE_SEL_SINGLE_PORT) || + (pinfo->mode_sel_state == MODE_GPIO_HIGH)) + out = true; + else if ((pinfo->mode_sel_state == MODE_SEL_DUAL_PORT) + || (pinfo->mode_sel_state == MODE_GPIO_LOW)) + out = false; + + rc = gpio_direction_output( + ctrl_pdata->lcd_mode_sel_gpio, out); + if (rc) { + pr_err("%s: unable to set dir for mode gpio\n", + __func__); + goto exit; + } + } + + if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { + pr_debug("%s: Panel Not properly turned OFF\n", + __func__); + ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; + pr_debug("%s: Reset panel done\n", __func__); + } + } else { + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) + gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); + else + gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); + + gpio_free(ctrl_pdata->avdd_en_gpio); + } + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + gpio_set_value((ctrl_pdata->disp_en_gpio), 0); + gpio_free(ctrl_pdata->disp_en_gpio); + } + gpio_set_value((ctrl_pdata->rst_gpio), 0); + gpio_free(ctrl_pdata->rst_gpio); + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + gpio_set_value(ctrl_pdata->lcd_mode_sel_gpio, 0); + gpio_free(ctrl_pdata->lcd_mode_sel_gpio); + } + } + + exit: + return rc; + } + + int mdss_dsi_px_clk_req(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (enable) { + if(!IS_ERR(ctrl_pdata->px_clk_src) && (!ctrl_pdata->px_clk_enabled)){ + clk_set_rate(ctrl_pdata->px_clk_src, 19200000); + rc = clk_prepare_enable(ctrl_pdata->px_clk_src); + if (rc){ + pr_err("px clk_prepare_enable failed, rc=%d\n", rc); + } + ctrl_pdata->px_clk_enabled = 1; + } + } else{ + if(!IS_ERR(ctrl_pdata->px_clk_src) && ctrl_pdata->px_clk_enabled){ + clk_disable_unprepare(ctrl_pdata->px_clk_src); + ctrl_pdata->px_clk_enabled = 0; + } + } + return rc; + } + int mdss_dsi_disp_vci_en(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->disp_vci_en_gpio)) { + pr_debug("%s:%d, vci_en_gpio line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: vci_en_gpio enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable) { + rc = gpio_request(ctrl_pdata->disp_vci_en_gpio, + "disp_vci_en"); + if (rc) { + pr_err("request vci_enable gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->disp_vci_en_gpio, 1); + } else { + gpio_set_value(ctrl_pdata->disp_vci_en_gpio, 0); + gpio_free(ctrl_pdata->disp_vci_en_gpio); + } + return rc; + } + int mdss_dsi_isp_1v1_en(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; + + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->isp_1v1_en_gpio)) { + pr_debug("%s:%d, isp_1v1_en line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: isp_1v1_en enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable) { + rc = gpio_request(ctrl_pdata->isp_1v1_en_gpio, + "isp_1v1_en_gpio"); + if (rc) { + pr_err("request isp_1v1 gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->isp_1v1_en_gpio, 1); + } else { + gpio_set_value(ctrl_pdata->isp_1v1_en_gpio, 0); + gpio_free(ctrl_pdata->isp_1v1_en_gpio); + } + return rc; + } + + int mdss_dsi_disp_poc_en(struct mdss_panel_data *pdata, int enable) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->disp_poc_en_gpio)) { + pr_debug("%s:%d, poc_en_gpio line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: poc_en_gpio enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable) { + rc = gpio_request(ctrl_pdata->disp_poc_en_gpio, + "disp_poc_en"); + if (rc) { + pr_err("request poc_enable gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->disp_poc_en_gpio, 1); + } else { + gpio_set_value(ctrl_pdata->disp_poc_en_gpio, 0); + gpio_free(ctrl_pdata->disp_poc_en_gpio); + } + return rc; + } + + + + /** + * mdss_dsi_roi_merge() - merge two roi into single roi + * + * Function used by partial update with only one dsi intf take 2A/2B + * (column/page) dcs commands. + */ + static int mdss_dsi_roi_merge(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi) + { + struct mdss_panel_info *l_pinfo; + struct mdss_rect *l_roi; + struct mdss_rect *r_roi; + struct mdss_dsi_ctrl_pdata *other = NULL; + int ans = 0; + + if (ctrl->ndx == DSI_CTRL_LEFT) { + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_RIGHT); + if (!other) + return ans; + l_pinfo = &(ctrl->panel_data.panel_info); + l_roi = &(ctrl->panel_data.panel_info.roi); + r_roi = &(other->panel_data.panel_info.roi); + } else { + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); + if (!other) + return ans; + l_pinfo = &(other->panel_data.panel_info); + l_roi = &(other->panel_data.panel_info.roi); + r_roi = &(ctrl->panel_data.panel_info.roi); + } + + if (l_roi->w == 0 && l_roi->h == 0) { + /* right only */ + *roi = *r_roi; + roi->x += l_pinfo->xres;/* add left full width to x-offset */ + } else { + /* left only and left+righ */ + *roi = *l_roi; + roi->w += r_roi->w; /* add right width */ + ans = 1; + } + + return ans; + } + + static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */ + static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */ + + /* + * Some panels can support multiple ROIs as part of the below commands + */ + static char caset_dual[] = {0x2a, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ + static char paset_dual[] = {0x2b, 0x00, 0x00, 0x05, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ + + /* pack into one frame before sent */ + static struct dsi_cmd_desc set_col_page_addr_cmd[] = { + {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset)}, caset}, /* packed */ + {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset}, + }; + + /* pack into one frame before sent */ + static struct dsi_cmd_desc set_dual_col_page_addr_cmd[] = { /*packed*/ + {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset_dual)}, caset_dual}, + {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset_dual)}, paset_dual}, + }; + + + static void __mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi, bool dual_roi) + { + if (dual_roi) { + struct mdss_rect *first, *second; + + first = &ctrl->panel_data.panel_info.dual_roi.first_roi; + second = &ctrl->panel_data.panel_info.dual_roi.second_roi; + + caset_dual[1] = (((first->x) & 0xFF00) >> 8); + caset_dual[2] = (((first->x) & 0xFF)); + caset_dual[3] = (((first->x - 1 + first->w) & 0xFF00) >> 8); + caset_dual[4] = (((first->x - 1 + first->w) & 0xFF)); + /* skip the MPU setting byte*/ + caset_dual[6] = (((second->x) & 0xFF00) >> 8); + caset_dual[7] = (((second->x) & 0xFF)); + caset_dual[8] = (((second->x - 1 + second->w) & 0xFF00) >> 8); + caset_dual[9] = (((second->x - 1 + second->w) & 0xFF)); + set_dual_col_page_addr_cmd[0].payload = caset_dual; + + paset_dual[1] = (((first->y) & 0xFF00) >> 8); + paset_dual[2] = (((first->y) & 0xFF)); + paset_dual[3] = (((first->y - 1 + first->h) & 0xFF00) >> 8); + paset_dual[4] = (((first->y - 1 + first->h) & 0xFF)); + /* skip the MPU setting byte */ + paset_dual[6] = (((second->y) & 0xFF00) >> 8); + paset_dual[7] = (((second->y) & 0xFF)); + paset_dual[8] = (((second->y - 1 + second->h) & 0xFF00) >> 8); + paset_dual[9] = (((second->y - 1 + second->h) & 0xFF)); + set_dual_col_page_addr_cmd[1].payload = paset_dual; + } else { + caset[1] = (((roi->x) & 0xFF00) >> 8); + caset[2] = (((roi->x) & 0xFF)); + caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8); + caset[4] = (((roi->x - 1 + roi->w) & 0xFF)); + set_col_page_addr_cmd[0].payload = caset; + + paset[1] = (((roi->y) & 0xFF00) >> 8); + paset[2] = (((roi->y) & 0xFF)); + paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8); + paset[4] = (((roi->y - 1 + roi->h) & 0xFF)); + set_col_page_addr_cmd[1].payload = paset; + } + pr_debug("%s Sending 2A 2B cmnd with dual_roi=%d\n", __func__, + dual_roi); + + } + static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi, int unicast) + { + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + bool dual_roi = pinfo->dual_roi.enabled; + + __mdss_dsi_send_col_page_addr(ctrl, roi, dual_roi); + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds_cnt = 2; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; + if (unicast) + cmdreq.flags |= CMD_REQ_UNICAST; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + /* Send default or dual roi 2A/2B cmd */ + cmdreq.cmds = dual_roi ? set_dual_col_page_addr_cmd : + set_col_page_addr_cmd; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); + } + + static int mdss_dsi_set_col_page_addr(struct mdss_panel_data *pdata, + bool force_send) + { + struct mdss_panel_info *pinfo; + struct mdss_rect roi = {0}; + struct mdss_rect *p_roi; + struct mdss_rect *c_roi; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_dsi_ctrl_pdata *other = NULL; + int left_or_both = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pinfo = &pdata->panel_info; + p_roi = &pinfo->roi; + + /* + * to avoid keep sending same col_page info to panel, + * if roi_merge enabled, the roi of left ctrl is used + * to compare against new merged roi and saved new + * merged roi to it after comparing. + * if roi_merge disabled, then the calling ctrl's roi + * and pinfo's roi are used to compare. + */ + if (pinfo->partial_update_roi_merge) { + left_or_both = mdss_dsi_roi_merge(ctrl, &roi); + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); + c_roi = &other->roi; + } else { + c_roi = &ctrl->roi; + roi = *p_roi; + } + + /* roi had changed, do col_page update */ + if (force_send || !mdss_rect_cmp(c_roi, &roi)) { + pr_debug("%s: ndx=%d x=%d y=%d w=%d h=%d\n", + __func__, ctrl->ndx, p_roi->x, + p_roi->y, p_roi->w, p_roi->h); + + *c_roi = roi; /* keep to ctrl */ + if (c_roi->w == 0 || c_roi->h == 0) { + /* no new frame update */ + pr_debug("%s: ctrl=%d, no partial roi set\n", + __func__, ctrl->ndx); + return 0; + } + + if (pinfo->dcs_cmd_by_left) { + if (left_or_both && ctrl->ndx == DSI_CTRL_RIGHT) { + /* 2A/2B sent by left already */ + return 0; + } + } + + if (!mdss_dsi_sync_wait_enable(ctrl)) { + if (pinfo->dcs_cmd_by_left) + ctrl = mdss_dsi_get_ctrl_by_index( + DSI_CTRL_LEFT); + mdss_dsi_send_col_page_addr(ctrl, &roi, 0); + } else { + /* + * when sync_wait_broadcast enabled, + * need trigger at right ctrl to + * start both dcs cmd transmission + */ + other = mdss_dsi_get_other_ctrl(ctrl); + if (!other) + goto end; + + if (mdss_dsi_is_left_ctrl(ctrl)) { + if (pinfo->partial_update_roi_merge) { + /* + * roi is the one after merged + * to dsi-1 only + */ + mdss_dsi_send_col_page_addr(other, + &roi, 0); + } else { + mdss_dsi_send_col_page_addr(ctrl, + &ctrl->roi, 1); + mdss_dsi_send_col_page_addr(other, + &other->roi, 1); + } + } else { + if (pinfo->partial_update_roi_merge) { + /* + * roi is the one after merged + * to dsi-1 only + */ + mdss_dsi_send_col_page_addr(ctrl, + &roi, 0); + } else { + mdss_dsi_send_col_page_addr(other, + &other->roi, 1); + mdss_dsi_send_col_page_addr(ctrl, + &ctrl->roi, 1); + } + } + } + } + + end: + return 0; + } + + static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, + u32 mode) + { + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct dsi_panel_cmds *lp_on_cmds; + struct dsi_panel_cmds *lp_off_cmds; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + lp_on_cmds = &ctrl->lp_on_cmds; + lp_off_cmds = &ctrl->lp_off_cmds; + + /* Apply display settings for low-persistence mode */ + if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); + else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); + else + return -EINVAL; + + pr_debug("%s: Persistence mode %d applied\n", __func__, mode); + return 0; + } + + static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, + int mode) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mipi_panel_info *mipi; + struct dsi_panel_cmds *pcmds; + u32 flags = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + mipi = &pdata->panel_info.mipi; + + if (!mipi->dms_mode) + return; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (mipi->dms_mode != DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) { + flags |= CMD_REQ_COMMIT; + if (mode == SWITCH_TO_CMD_MODE) + pcmds = &ctrl_pdata->video2cmd; else - val = 1; - - rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); - if (rc) { - pr_err("%s: unable to set dir for bklt gpio val %d\n", - __func__, val); - goto free; - } - ctrl_pdata->bklt_en_gpio_state = true; - goto ret; - } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { - /* - * if gpio state is true and enable (bl level) is - * zero then toggle the gpio - */ - if (ctrl_pdata->bklt_en_gpio_invert) - val = 1; - else - val = 0; - - rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); - if (rc) - pr_err("%s: unable to set dir for bklt gpio val %d\n", - __func__, val); - goto free; - } - - /* gpio state is true and bl level is non zero */ - goto ret; - -free: - pr_debug("%s: free bklt gpio\n", __func__); - ctrl_pdata->bklt_en_gpio_state = false; - gpio_free(ctrl_pdata->bklt_en_gpio); -ret: - return rc; -} - -int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) + pcmds = &ctrl_pdata->cmd2video; + } else if ((mipi->dms_mode == + DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) + && pdata->current_timing + && !list_empty(&pdata->timings_list)) { + struct dsi_panel_timing *pt; + + pt = container_of(pdata->current_timing, + struct dsi_panel_timing, timing); + + pr_debug("%s: sending switch commands\n", __func__); + pcmds = &pt->switch_cmds; + flags |= CMD_REQ_DMA_TPG; + flags |= CMD_REQ_COMMIT; + } else { + pr_warn("%s: Invalid mode switch attempted\n", __func__); + return; + } + + if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && + (pdata->panel_info.send_pps_before_switch)) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + + mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds, flags); + + if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && + (!pdata->panel_info.send_pps_before_switch)) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + } +/************************************ +backlight level 0-->55 ==> 0-->55 +backlight level 55-->230 ==> 55-->200 +backlight level 230-->255 ==> 200-->255 +*************************************/ +static u32 backlight_level_remap(struct mdss_dsi_ctrl_pdata *ctrl, u32 level) { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo = NULL; - int i, rc = 0; + u32 remap_level = 0; - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pinfo = &(ctrl_pdata->panel_data.panel_info); - if ((mdss_dsi_is_right_ctrl(ctrl_pdata) && - mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) || - pinfo->is_dba_panel) { - pr_debug("%s:%d, right ctrl gpio configuration not needed\n", - __func__, __LINE__); - return rc; - } - - if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - pr_debug("%s:%d, reset line not configured\n", - __func__, __LINE__); - } - - if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { - pr_debug("%s:%d, reset line not configured\n", - __func__, __LINE__); - return rc; - } - - pr_debug("%s: enable = %d\n", __func__, enable); - - if (enable) { - rc = mdss_dsi_request_gpios(ctrl_pdata); - if (rc) { - pr_err("gpio request failed\n"); - return rc; - } - if (!pinfo->cont_splash_enabled) { - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - rc = gpio_direction_output( - ctrl_pdata->disp_en_gpio, 1); - if (rc) { - pr_err("%s: unable to set dir for en gpio\n", - __func__); - goto exit; - } - } - - if (pdata->panel_info.rst_seq_len) { - rc = gpio_direction_output(ctrl_pdata->rst_gpio, - pdata->panel_info.rst_seq[0]); - if (rc) { - pr_err("%s: unable to set dir for rst gpio\n", - __func__); - goto exit; - } - } - - for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { - gpio_set_value((ctrl_pdata->rst_gpio), - pdata->panel_info.rst_seq[i]); - if (pdata->panel_info.rst_seq[++i]) - usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); - } - - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - if (ctrl_pdata->avdd_en_gpio_invert) { - rc = gpio_direction_output( - ctrl_pdata->avdd_en_gpio, 0); - } else { - rc = gpio_direction_output( - ctrl_pdata->avdd_en_gpio, 1); - } - if (rc) { - pr_err("%s: unable to set dir for avdd_en gpio\n", - __func__); - goto exit; - } - } - } - - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - bool out = false; - - if ((pinfo->mode_sel_state == MODE_SEL_SINGLE_PORT) || - (pinfo->mode_sel_state == MODE_GPIO_HIGH)) - out = true; - else if ((pinfo->mode_sel_state == MODE_SEL_DUAL_PORT) - || (pinfo->mode_sel_state == MODE_GPIO_LOW)) - out = false; - - rc = gpio_direction_output( - ctrl_pdata->lcd_mode_sel_gpio, out); - if (rc) { - pr_err("%s: unable to set dir for mode gpio\n", - __func__); - goto exit; - } - } - - if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { - pr_debug("%s: Panel Not properly turned OFF\n", - __func__); - ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; - pr_debug("%s: Reset panel done\n", __func__); - } - } else { - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - if (ctrl_pdata->avdd_en_gpio_invert) - gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); - else - gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); - - gpio_free(ctrl_pdata->avdd_en_gpio); - } - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - gpio_set_value((ctrl_pdata->disp_en_gpio), 0); - gpio_free(ctrl_pdata->disp_en_gpio); - } - gpio_set_value((ctrl_pdata->rst_gpio), 0); - gpio_free(ctrl_pdata->rst_gpio); - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - gpio_set_value(ctrl_pdata->lcd_mode_sel_gpio, 0); - gpio_free(ctrl_pdata->lcd_mode_sel_gpio); - } - } - -exit: - return rc; + if (ctrl->bklt_max == 255){ + if (level < 55){ + remap_level = level; + } else if ((level >= 55) && (level <= 230)){ + remap_level = (level*29+330)/35; + }else{ + remap_level = level*11/5-306; + } + } else{ + remap_level = level; + } + return remap_level; } + static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, + u32 bl_level) + { + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_dsi_ctrl_pdata *sctrl = NULL; + static bool first_bl_level = true; -/** - * mdss_dsi_roi_merge() - merge two roi into single roi - * - * Function used by partial update with only one dsi intf take 2A/2B - * (column/page) dcs commands. - */ -static int mdss_dsi_roi_merge(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi) + if (first_bl_level || (bl_level == 0)){ + printk("---backlight level = %d---\n", bl_level); + first_bl_level = (bl_level == 0)? true : false; + } + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (ctrl_pdata->high_brightness_panel){ + pr_debug("%s goto backlight level remap\n", __func__); + bl_level = backlight_level_remap(ctrl_pdata, bl_level); + } + /* + * Some backlight controllers specify a minimum duty cycle + * for the backlight brightness. If the brightness is less + * than it, the controller can malfunction. + */ + pr_debug("%s: bl_level:%d\n", __func__, bl_level); + + /* do not allow backlight to change when panel in disable mode */ + if (pdata->panel_disable_mode && (bl_level != 0)) + return; + + if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) + bl_level = pdata->panel_info.bl_min; + + /* enable the backlight gpio if present */ + mdss_dsi_bl_gpio_ctrl(pdata, bl_level); + + switch (ctrl_pdata->bklt_ctrl) { + case BL_WLED: + led_trigger_event(bl_led_trigger, bl_level); + break; + case BL_PWM: + mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level); + break; + case BL_DCS_CMD: + if (!mdss_dsi_sync_wait_enable(ctrl_pdata)) { + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + break; + } + /* + * DCS commands to update backlight are usually sent at + * the same time to both the controllers. However, if + * sync_wait is enabled, we need to ensure that the + * dcs commands are first sent to the non-trigger + * controller so that when the commands are triggered, + * both controllers receive it at the same time. + */ + sctrl = mdss_dsi_get_other_ctrl(ctrl_pdata); + if (mdss_dsi_sync_wait_trigger(ctrl_pdata)) { + if (sctrl) + mdss_dsi_panel_bklt_dcs(sctrl, bl_level); + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + } else { + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + if (sctrl) + mdss_dsi_panel_bklt_dcs(sctrl, bl_level); + } + break; + default: + pr_err("%s: Unknown bl_ctrl configuration\n", + __func__); + break; + } + } + + +int mdss_dsi_panel_set_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) { - struct mdss_panel_info *l_pinfo; - struct mdss_rect *l_roi; - struct mdss_rect *r_roi; - struct mdss_dsi_ctrl_pdata *other = NULL; - int ans = 0; + struct dsi_panel_cmds *srgb_on_cmds; + struct dsi_panel_cmds *srgb_off_cmds; - if (ctrl->ndx == DSI_CTRL_LEFT) { - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_RIGHT); - if (!other) - return ans; - l_pinfo = &(ctrl->panel_data.panel_info); - l_roi = &(ctrl->panel_data.panel_info.roi); - r_roi = &(other->panel_data.panel_info.roi); - } else { - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); - if (!other) - return ans; - l_pinfo = &(other->panel_data.panel_info); - l_roi = &(other->panel_data.panel_info.roi); - r_roi = &(ctrl->panel_data.panel_info.roi); + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; } - - if (l_roi->w == 0 && l_roi->h == 0) { - /* right only */ - *roi = *r_roi; - roi->x += l_pinfo->xres;/* add left full width to x-offset */ - } else { - /* left only and left+righ */ - *roi = *l_roi; - roi->w += r_roi->w; /* add right width */ - ans = 1; - } - - return ans; -} - -static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */ -static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */ - -/* - * Some panels can support multiple ROIs as part of the below commands - */ -static char caset_dual[] = {0x2a, 0x00, 0x00, 0x03, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ -static char paset_dual[] = {0x2b, 0x00, 0x00, 0x05, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ - -/* pack into one frame before sent */ -static struct dsi_cmd_desc set_col_page_addr_cmd[] = { - {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset)}, caset}, /* packed */ - {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset}, -}; - -/* pack into one frame before sent */ -static struct dsi_cmd_desc set_dual_col_page_addr_cmd[] = { /*packed*/ - {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset_dual)}, caset_dual}, - {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset_dual)}, paset_dual}, -}; - - -static void __mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi, bool dual_roi) -{ - if (dual_roi) { - struct mdss_rect *first, *second; - - first = &ctrl->panel_data.panel_info.dual_roi.first_roi; - second = &ctrl->panel_data.panel_info.dual_roi.second_roi; - - caset_dual[1] = (((first->x) & 0xFF00) >> 8); - caset_dual[2] = (((first->x) & 0xFF)); - caset_dual[3] = (((first->x - 1 + first->w) & 0xFF00) >> 8); - caset_dual[4] = (((first->x - 1 + first->w) & 0xFF)); - /* skip the MPU setting byte*/ - caset_dual[6] = (((second->x) & 0xFF00) >> 8); - caset_dual[7] = (((second->x) & 0xFF)); - caset_dual[8] = (((second->x - 1 + second->w) & 0xFF00) >> 8); - caset_dual[9] = (((second->x - 1 + second->w) & 0xFF)); - set_dual_col_page_addr_cmd[0].payload = caset_dual; - - paset_dual[1] = (((first->y) & 0xFF00) >> 8); - paset_dual[2] = (((first->y) & 0xFF)); - paset_dual[3] = (((first->y - 1 + first->h) & 0xFF00) >> 8); - paset_dual[4] = (((first->y - 1 + first->h) & 0xFF)); - /* skip the MPU setting byte */ - paset_dual[6] = (((second->y) & 0xFF00) >> 8); - paset_dual[7] = (((second->y) & 0xFF)); - paset_dual[8] = (((second->y - 1 + second->h) & 0xFF00) >> 8); - paset_dual[9] = (((second->y - 1 + second->h) & 0xFF)); - set_dual_col_page_addr_cmd[1].payload = paset_dual; - } else { - caset[1] = (((roi->x) & 0xFF00) >> 8); - caset[2] = (((roi->x) & 0xFF)); - caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8); - caset[4] = (((roi->x - 1 + roi->w) & 0xFF)); - set_col_page_addr_cmd[0].payload = caset; - - paset[1] = (((roi->y) & 0xFF00) >> 8); - paset[2] = (((roi->y) & 0xFF)); - paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8); - paset[4] = (((roi->y - 1 + roi->h) & 0xFF)); - set_col_page_addr_cmd[1].payload = paset; - } - pr_debug("%s Sending 2A 2B cmnd with dual_roi=%d\n", __func__, - dual_roi); - -} -static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi, int unicast) -{ - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - bool dual_roi = pinfo->dual_roi.enabled; - - __mdss_dsi_send_col_page_addr(ctrl, roi, dual_roi); - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds_cnt = 2; - cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; - if (unicast) - cmdreq.flags |= CMD_REQ_UNICAST; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - /* Send default or dual roi 2A/2B cmd */ - cmdreq.cmds = dual_roi ? set_dual_col_page_addr_cmd : - set_col_page_addr_cmd; - mdss_dsi_cmdlist_put(ctrl, &cmdreq); -} - -static int mdss_dsi_set_col_page_addr(struct mdss_panel_data *pdata, - bool force_send) -{ - struct mdss_panel_info *pinfo; - struct mdss_rect roi = {0}; - struct mdss_rect *p_roi; - struct mdss_rect *c_roi; - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_dsi_ctrl_pdata *other = NULL; - int left_or_both = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pinfo = &pdata->panel_info; - p_roi = &pinfo->roi; - - /* - * to avoid keep sending same col_page info to panel, - * if roi_merge enabled, the roi of left ctrl is used - * to compare against new merged roi and saved new - * merged roi to it after comparing. - * if roi_merge disabled, then the calling ctrl's roi - * and pinfo's roi are used to compare. - */ - if (pinfo->partial_update_roi_merge) { - left_or_both = mdss_dsi_roi_merge(ctrl, &roi); - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); - c_roi = &other->roi; - } else { - c_roi = &ctrl->roi; - roi = *p_roi; - } - - /* roi had changed, do col_page update */ - if (force_send || !mdss_rect_cmp(c_roi, &roi)) { - pr_debug("%s: ndx=%d x=%d y=%d w=%d h=%d\n", - __func__, ctrl->ndx, p_roi->x, - p_roi->y, p_roi->w, p_roi->h); - - *c_roi = roi; /* keep to ctrl */ - if (c_roi->w == 0 || c_roi->h == 0) { - /* no new frame update */ - pr_debug("%s: ctrl=%d, no partial roi set\n", - __func__, ctrl->ndx); - return 0; - } - - if (pinfo->dcs_cmd_by_left) { - if (left_or_both && ctrl->ndx == DSI_CTRL_RIGHT) { - /* 2A/2B sent by left already */ - return 0; - } - } - - if (!mdss_dsi_sync_wait_enable(ctrl)) { - if (pinfo->dcs_cmd_by_left) - ctrl = mdss_dsi_get_ctrl_by_index( - DSI_CTRL_LEFT); - mdss_dsi_send_col_page_addr(ctrl, &roi, 0); + srgb_on_cmds = &ctrl->srgb_on_cmds; + srgb_off_cmds = &ctrl->srgb_off_cmds; + if (level) { + if (srgb_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, srgb_on_cmds, + CMD_REQ_COMMIT); + pr_err("sRGB Mode On.\n"); } else { - /* - * when sync_wait_broadcast enabled, - * need trigger at right ctrl to - * start both dcs cmd transmission - */ - other = mdss_dsi_get_other_ctrl(ctrl); - if (!other) - goto end; + pr_err("This panel not support sRGB mode on.\n"); + } + } else { + if (srgb_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, + srgb_off_cmds, CMD_REQ_COMMIT); + pr_err("sRGB Mode off.\n"); + } else { + pr_err("This panel not support sRGB mode off.\n"); + } + } + mutex_unlock(&ctrl->panel_mode_lock); + return 0; +} +int mdss_dsi_panel_get_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl) +{ + return ctrl->SRGB_mode; +} - if (mdss_dsi_is_left_ctrl(ctrl)) { - if (pinfo->partial_update_roi_merge) { - /* - * roi is the one after merged - * to dsi-1 only - */ - mdss_dsi_send_col_page_addr(other, - &roi, 0); - } else { - mdss_dsi_send_col_page_addr(ctrl, - &ctrl->roi, 1); - mdss_dsi_send_col_page_addr(other, - &other->roi, 1); - } - } else { - if (pinfo->partial_update_roi_merge) { - /* - * roi is the one after merged - * to dsi-1 only - */ - mdss_dsi_send_col_page_addr(ctrl, - &roi, 0); - } else { - mdss_dsi_send_col_page_addr(other, - &other->roi, 1); - mdss_dsi_send_col_page_addr(ctrl, - &ctrl->roi, 1); - } - } +int mdss_dsi_panel_set_adobe_rgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, + int level) +{ + struct dsi_panel_cmds *adobe_rgb_on_cmds; + struct dsi_panel_cmds *adobe_rgb_off_cmds; + + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; + } + adobe_rgb_on_cmds = &ctrl->Adobe_RGB_on_cmds; + adobe_rgb_off_cmds = &ctrl->Adobe_RGB_off_cmds; + if (level) { + if (adobe_rgb_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, adobe_rgb_on_cmds, + CMD_REQ_COMMIT); + pr_err("Adobe RGB Mode On.\n"); + } else { + pr_err("This Panel not support Adobe RGB mode On.\n"); + } + } else { + if (adobe_rgb_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, adobe_rgb_off_cmds, + CMD_REQ_COMMIT); + pr_err("Adobe RGB Mode Off.\n"); + } else { + pr_err("This Panel not support Adobe RGB mode Off.\n"); } } + mutex_unlock(&ctrl->panel_mode_lock); + return 0; +} +int mdss_dsi_panel_get_adobe_rgb_mode(struct mdss_dsi_ctrl_pdata *ctrl) +{ + return ctrl->Adobe_RGB_mode; +} -end: +int mdss_dsi_panel_set_dci_p3_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) +{ + struct dsi_panel_cmds *dci_p3_on_cmds; + struct dsi_panel_cmds *dci_p3_off_cmds; + + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; + } + dci_p3_on_cmds = &ctrl->dci_p3_on_cmds; + dci_p3_off_cmds = &ctrl->dci_p3_off_cmds; + if (level) { + if (dci_p3_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, dci_p3_on_cmds, + CMD_REQ_COMMIT); + pr_err("DCI-P3 Mode On.\n"); + } else { + pr_err("This Panel not support DCI-P3 mode On.\n"); + } + } else { + if (dci_p3_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, dci_p3_off_cmds, + CMD_REQ_COMMIT); + pr_err("DCI-P3 Mode Off.\n"); + } else { + pr_err("This Panel not support DCI-P3 mode Off.\n"); + } + } + mutex_unlock(&ctrl->panel_mode_lock); return 0; } -static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, - u32 mode) +int mdss_dsi_panel_get_dci_p3_mode(struct mdss_dsi_ctrl_pdata *ctrl) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct dsi_panel_cmds *lp_on_cmds; - struct dsi_panel_cmds *lp_off_cmds; + return ctrl->dci_p3_mode; +} - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; +int mdss_dsi_panel_set_night_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) +{ + struct dsi_panel_cmds *night_mode_on_cmds; + struct dsi_panel_cmds *night_mode_off_cmds; + + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; } - - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - lp_on_cmds = &ctrl->lp_on_cmds; - lp_off_cmds = &ctrl->lp_off_cmds; - - /* Apply display settings for low-persistence mode */ - if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && - (lp_on_cmds->cmd_cnt)) - mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); - else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && - (lp_on_cmds->cmd_cnt)) - mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); - else - return -EINVAL; - - pr_debug("%s: Persistence mode %d applied\n", __func__, mode); + night_mode_on_cmds = &ctrl->night_mode_on_cmds; + /* night mode same as sRGB mode */ + night_mode_off_cmds = &ctrl->night_mode_off_cmds; + /* night mode same as sRGB mode */ + if (level) { + if (night_mode_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, + night_mode_on_cmds, CMD_REQ_COMMIT); + pr_err("Night Mode On (night mode).\n"); + } else { + pr_err("This panel not support Night mode on (night mode).\n"); + } + } else { + if (night_mode_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, + night_mode_off_cmds, CMD_REQ_COMMIT); + pr_err("Night Mode off (night mode).\n"); + } else { + pr_err("This panel not support night mode off (night mode).\n"); + } + } + mutex_unlock(&ctrl->panel_mode_lock); return 0; } - -static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, - int mode) +int mdss_dsi_panel_get_night_mode(struct mdss_dsi_ctrl_pdata *ctrl) { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mipi_panel_info *mipi; - struct dsi_panel_cmds *pcmds; - u32 flags = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return; - } - - mipi = &pdata->panel_info.mipi; - - if (!mipi->dms_mode) - return; - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - if (mipi->dms_mode != DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) { - flags |= CMD_REQ_COMMIT; - if (mode == SWITCH_TO_CMD_MODE) - pcmds = &ctrl_pdata->video2cmd; - else - pcmds = &ctrl_pdata->cmd2video; - } else if ((mipi->dms_mode == - DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) - && pdata->current_timing - && !list_empty(&pdata->timings_list)) { - struct dsi_panel_timing *pt; - - pt = container_of(pdata->current_timing, - struct dsi_panel_timing, timing); - - pr_debug("%s: sending switch commands\n", __func__); - pcmds = &pt->switch_cmds; - flags |= CMD_REQ_DMA_TPG; - flags |= CMD_REQ_COMMIT; - } else { - pr_warn("%s: Invalid mode switch attempted\n", __func__); - return; - } - - if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && - (pdata->panel_info.send_pps_before_switch)) - mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); - - mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds, flags); - - if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && - (!pdata->panel_info.send_pps_before_switch)) - mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + return ctrl->night_mode; } -static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, - u32 bl_level) +int mdss_dsi_panel_set_oneplus_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_dsi_ctrl_pdata *sctrl = NULL; + struct dsi_panel_cmds *oneplus_mode_on_cmds; + struct dsi_panel_cmds *oneplus_mode_off_cmds; - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return; + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; } + oneplus_mode_on_cmds = &ctrl->oneplus_mode_on_cmds; + /* night mode same as sRGB mode */ + oneplus_mode_off_cmds = &ctrl->oneplus_mode_off_cmds; + /* night mode same as sRGB mode */ - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - /* - * Some backlight controllers specify a minimum duty cycle - * for the backlight brightness. If the brightness is less - * than it, the controller can malfunction. - */ - pr_debug("%s: bl_level:%d\n", __func__, bl_level); - - /* do not allow backlight to change when panel in disable mode */ - if (pdata->panel_disable_mode && (bl_level != 0)) - return; - - if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) - bl_level = pdata->panel_info.bl_min; - - /* enable the backlight gpio if present */ - mdss_dsi_bl_gpio_ctrl(pdata, bl_level); - - switch (ctrl_pdata->bklt_ctrl) { - case BL_WLED: - led_trigger_event(bl_led_trigger, bl_level); - break; - case BL_PWM: - mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level); - break; - case BL_DCS_CMD: - if (!mdss_dsi_sync_wait_enable(ctrl_pdata)) { - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); - break; - } - /* - * DCS commands to update backlight are usually sent at - * the same time to both the controllers. However, if - * sync_wait is enabled, we need to ensure that the - * dcs commands are first sent to the non-trigger - * controller so that when the commands are triggered, - * both controllers receive it at the same time. - */ - sctrl = mdss_dsi_get_other_ctrl(ctrl_pdata); - if (mdss_dsi_sync_wait_trigger(ctrl_pdata)) { - if (sctrl) - mdss_dsi_panel_bklt_dcs(sctrl, bl_level); - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + if (level) { + if (oneplus_mode_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, oneplus_mode_on_cmds, + CMD_REQ_COMMIT); + pr_err("oneplus Mode On (oneplus mode).\n"); } else { - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); - if (sctrl) - mdss_dsi_panel_bklt_dcs(sctrl, bl_level); + pr_err("This panel not support oneplus mode on (oneplus mode).\n"); + } + } else { + if (oneplus_mode_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, oneplus_mode_off_cmds, + CMD_REQ_COMMIT); + pr_err("oneplus Mode off (oneplus mode).\n"); + } else { + pr_err("This panel not support oneplus mode off (oneplus mode).\n"); } - break; - default: - pr_err("%s: Unknown bl_ctrl configuration\n", - __func__); - break; } + mutex_unlock(&ctrl->panel_mode_lock); + + return 0; } +int mdss_dsi_panel_get_oneplus_mode(struct mdss_dsi_ctrl_pdata *ctrl) +{ + return ctrl->oneplus_mode; +} + +int mdss_dsi_panel_set_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl, + int level) +{ + struct dsi_panel_cmds *adaption_mode_on_cmds; + struct dsi_panel_cmds *adaption_mode_off_cmds; + + mutex_lock(&ctrl->panel_mode_lock); + if (!ctrl->is_panel_on) { + mutex_unlock(&ctrl->panel_mode_lock); + return 0; + } + adaption_mode_on_cmds = &ctrl->adaption_mode_on_cmds; + /* night mode same as sRGB mode */ + adaption_mode_off_cmds = &ctrl->adaption_mode_off_cmds; + /* night mode same as sRGB mode */ + if (level) { + if (adaption_mode_on_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, adaption_mode_on_cmds, + CMD_REQ_COMMIT); + pr_err("Adaption Mode On (adaption mode).\n"); + } else { + pr_err("This panel not support Adaption mode on (adaption mode).\n"); + } + } else { + if (adaption_mode_off_cmds->cmd_cnt) { + mdss_dsi_panel_cmds_send(ctrl, adaption_mode_off_cmds, + CMD_REQ_COMMIT); + pr_err("Adaption Mode off (adaption mode).\n"); + } else { + pr_err("This panel not support adaption mode off (adaption mode).\n"); + } + } + mutex_unlock(&ctrl->panel_mode_lock); + return 0; +} +int mdss_dsi_panel_get_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl) +{ + return ctrl->adaption_mode; +} + static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) { @@ -923,13 +1368,13 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) int ret = 0; if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; } - + pr_err("%s start\n", __func__); pinfo = &pdata->panel_info; ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); + panel_data); pr_debug("%s: ndx=%d\n", __func__, ctrl->ndx); @@ -941,170 +1386,227 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) on_cmds = &ctrl->on_cmds; if ((pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE) && - (pinfo->mipi.boot_mode != pinfo->mipi.mode)) + (pinfo->mipi.boot_mode != pinfo->mipi.mode)) on_cmds = &ctrl->post_dms_on_cmds; pr_debug("%s: ndx=%d cmd_cnt=%d\n", __func__, - ctrl->ndx, on_cmds->cmd_cnt); + ctrl->ndx, on_cmds->cmd_cnt); + mutex_lock(&ctrl->panel_mode_lock); + ctrl->is_panel_on = true; + mutex_unlock(&ctrl->panel_mode_lock); + if (mdss_dsi_panel_get_srgb_mode(ctrl)) { + mdss_dsi_panel_set_srgb_mode(ctrl, + mdss_dsi_panel_get_srgb_mode(ctrl)); + } + + if (mdss_dsi_panel_get_adobe_rgb_mode(ctrl)) { + mdss_dsi_panel_set_adobe_rgb_mode(ctrl, + mdss_dsi_panel_get_adobe_rgb_mode(ctrl)); + } + + if (mdss_dsi_panel_get_dci_p3_mode(ctrl)) { + mdss_dsi_panel_set_dci_p3_mode(ctrl, + mdss_dsi_panel_get_dci_p3_mode(ctrl)); + } + + if (mdss_dsi_panel_get_night_mode(ctrl)) { + mdss_dsi_panel_set_night_mode(ctrl, + mdss_dsi_panel_get_night_mode(ctrl)); + } + if (mdss_dsi_panel_get_oneplus_mode(ctrl)) { + mdss_dsi_panel_set_oneplus_mode(ctrl, + mdss_dsi_panel_get_oneplus_mode(ctrl)); + } + + if (mdss_dsi_panel_get_adaption_mode(ctrl)) { + mdss_dsi_panel_set_adaption_mode(ctrl, + mdss_dsi_panel_get_adaption_mode(ctrl)); + } + +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) +#if !defined(WITHOUT_IRIS) + iris_init(ctrl); +#endif + if (on_cmds->cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); +#if !defined(WITHOUT_IRIS) + iris_lightup(ctrl); +#endif +#else if (on_cmds->cmd_cnt) mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); - - if (pinfo->compression_mode == COMPRESSION_DSC) - mdss_dsi_panel_dsc_pps_send(ctrl, pinfo); - - if (ctrl->ds_registered) - mdss_dba_utils_video_on(pinfo->dba_data, pinfo); - - /* Ensure low persistence mode is set as before */ - mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); - -end: - pr_debug("%s:-\n", __func__); - return ret; +#endif + + + if (pinfo->compression_mode == COMPRESSION_DSC) + mdss_dsi_panel_dsc_pps_send(ctrl, pinfo); + + if (ctrl->ds_registered) + mdss_dba_utils_video_on(pinfo->dba_data, pinfo); + + /* Ensure low persistence mode is set as before */ + mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); + + end: + pr_debug("%s:-\n", __func__); + pr_err("%s end\n", __func__); + return ret; } - + static int mdss_dsi_post_panel_on(struct mdss_panel_data *pdata) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - struct dsi_panel_cmds *cmds; - u32 vsync_period = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); - - pinfo = &pdata->panel_info; - if (pinfo->dcs_cmd_by_left && ctrl->ndx != DSI_CTRL_LEFT) - goto end; - - cmds = &ctrl->post_panel_on_cmds; - if (cmds->cmd_cnt) { - msleep(VSYNC_DELAY); /* wait for a vsync passed */ - mdss_dsi_panel_cmds_send(ctrl, cmds, CMD_REQ_COMMIT); - } - - if (pinfo->is_dba_panel && pinfo->is_pluggable) { - /* ensure at least 1 frame transfers to down stream device */ - vsync_period = (MSEC_PER_SEC / pinfo->mipi.frame_rate) + 1; - msleep(vsync_period); - mdss_dba_utils_hdcp_enable(pinfo->dba_data, true); - } - -end: - pr_debug("%s:-\n", __func__); - return 0; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + struct dsi_panel_cmds *cmds; + u32 vsync_period = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + pr_err("%s start\n", __func__); + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); + + pinfo = &pdata->panel_info; + if (pinfo->dcs_cmd_by_left && ctrl->ndx != DSI_CTRL_LEFT) + goto end; + + cmds = &ctrl->post_panel_on_cmds; + if (cmds->cmd_cnt) { + msleep(VSYNC_DELAY); /* wait for a vsync passed */ + mdss_dsi_panel_cmds_send(ctrl, cmds, CMD_REQ_COMMIT); + } + + if (pinfo->is_dba_panel && pinfo->is_pluggable) { + /* ensure at least 1 frame transfers to down stream device */ + vsync_period = (MSEC_PER_SEC / pinfo->mipi.frame_rate) + 1; + msleep(vsync_period); + mdss_dba_utils_hdcp_enable(pinfo->dba_data, true); + } + + end: + pr_debug("%s:-\n", __func__); + pr_err("%s end\n", __func__); + return 0; } - + static int mdss_dsi_panel_off(struct mdss_panel_data *pdata) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - pinfo = &pdata->panel_info; - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); - - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - goto end; - } - - if (ctrl->off_cmds.cmd_cnt) - mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); - - if (ctrl->ds_registered && pinfo->is_pluggable) { - mdss_dba_utils_video_off(pinfo->dba_data); - mdss_dba_utils_hdcp_enable(pinfo->dba_data, false); - } - -end: - pr_debug("%s:-\n", __func__); - return 0; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + pr_err("%s start\n", __func__); + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); + + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + goto end; + } + mutex_lock(&ctrl->panel_mode_lock); + ctrl->is_panel_on = false; + mutex_unlock(&ctrl->panel_mode_lock); +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) +#if !defined(WITHOUT_IRIS) + iris_lightoff(ctrl); +#endif + if (ctrl->off_cmds.cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); +#else + if (ctrl->off_cmds.cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); +#endif + if (ctrl->ds_registered && pinfo->is_pluggable) { + mdss_dba_utils_video_off(pinfo->dba_data); + mdss_dba_utils_hdcp_enable(pinfo->dba_data, false); + } + + end: + pr_debug("%s:-\n", __func__); + pr_err("%s end\n", __func__); + return 0; } - + static int mdss_dsi_panel_low_power_config(struct mdss_panel_data *pdata, - int enable) + int enable) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - pinfo = &pdata->panel_info; - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d enable=%d\n", __func__, ctrl, ctrl->ndx, - enable); - - /* Any panel specific low power commands/config */ - - pr_debug("%s:-\n", __func__); - return 0; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d enable=%d\n", __func__, ctrl, ctrl->ndx, + enable); + + /* Any panel specific low power commands/config */ + + pr_debug("%s:-\n", __func__); + return 0; } - + static void mdss_dsi_parse_mdp_kickoff_threshold(struct device_node *np, - struct mdss_panel_info *pinfo) + struct mdss_panel_info *pinfo) { - int len, rc; - const u32 *src; - u32 tmp; - u32 max_delay_us; - - pinfo->mdp_koff_thshold = false; - src = of_get_property(np, "qcom,mdss-mdp-kickoff-threshold", &len); - if (!src || (len == 0)) - return; - - rc = of_property_read_u32(np, "qcom,mdss-mdp-kickoff-delay", &tmp); - if (!rc) - pinfo->mdp_koff_delay = tmp; - else - return; - - if (pinfo->mipi.frame_rate == 0) { - pr_err("cannot enable guard window, unexpected panel fps\n"); - return; - } - - pinfo->mdp_koff_thshold_low = be32_to_cpu(src[0]); - pinfo->mdp_koff_thshold_high = be32_to_cpu(src[1]); - max_delay_us = 1000000 / pinfo->mipi.frame_rate; - - /* enable the feature if threshold is valid */ - if ((pinfo->mdp_koff_thshold_low < pinfo->mdp_koff_thshold_high) && - ((pinfo->mdp_koff_delay > 0) || - (pinfo->mdp_koff_delay < max_delay_us))) - pinfo->mdp_koff_thshold = true; - - pr_debug("panel kickoff thshold:[%d, %d] delay:%d (max:%d) enable:%d\n", - pinfo->mdp_koff_thshold_low, - pinfo->mdp_koff_thshold_high, - pinfo->mdp_koff_delay, - max_delay_us, - pinfo->mdp_koff_thshold); + int len, rc; + const u32 *src; + u32 tmp; + u32 max_delay_us; + + pinfo->mdp_koff_thshold = false; + src = of_get_property(np, "qcom,mdss-mdp-kickoff-threshold", &len); + if (!src || (len == 0)) + return; + + rc = of_property_read_u32(np, "qcom,mdss-mdp-kickoff-delay", &tmp); + if (!rc) + pinfo->mdp_koff_delay = tmp; + else + return; + + if (pinfo->mipi.frame_rate == 0) { + pr_err("cannot enable guard window, unexpected panel fps\n"); + return; + } + + pinfo->mdp_koff_thshold_low = be32_to_cpu(src[0]); + pinfo->mdp_koff_thshold_high = be32_to_cpu(src[1]); + max_delay_us = 1000000 / pinfo->mipi.frame_rate; + + /* enable the feature if threshold is valid */ + if ((pinfo->mdp_koff_thshold_low < pinfo->mdp_koff_thshold_high) && + ((pinfo->mdp_koff_delay > 0) || + (pinfo->mdp_koff_delay < max_delay_us))) + pinfo->mdp_koff_thshold = true; + + pr_debug("panel kickoff thshold:[%d, %d] delay:%d (max:%d) enable:%d\n", + pinfo->mdp_koff_thshold_low, + pinfo->mdp_koff_thshold_high, + pinfo->mdp_koff_delay, + max_delay_us, + pinfo->mdp_koff_thshold); } - + static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger, - char *trigger_key) + char *trigger_key) { const char *data; @@ -1121,10 +1623,10 @@ static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger, *trigger = DSI_CMD_TRIGGER_SW_TE; } } - + static int mdss_dsi_parse_dcs_cmds(struct device_node *np, - struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) + struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) { const char *data; int blen = 0, len; @@ -1162,13 +1664,13 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np, len -= dchdr->dlen; cnt++; } - + if (len != 0) { pr_err("%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen); goto exit_free; } - + pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc), GFP_KERNEL); if (!pcmds->cmds) @@ -1189,7 +1691,7 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np, bp += dchdr->dlen; len -= dchdr->dlen; } - + /*Set default link state to LP Mode*/ pcmds->link_state = DSI_LP_MODE; @@ -1210,12 +1712,13 @@ exit_free: kfree(buf); return -ENOMEM; } - - + + int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, - char *dst_format) + char *dst_format) { int rc = 0; + switch (bpp) { case 3: *dst_format = DSI_CMD_DST_FORMAT_RGB111; @@ -1240,6 +1743,7 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, } break; case 18: + switch (mipi_mode) { case DSI_VIDEO_MODE: if (pixel_packing == 0) @@ -1277,1736 +1781,1803 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, } return rc; } - -static int mdss_dsi_parse_fbc_params(struct device_node *np, - struct mdss_panel_timing *timing) -{ - int rc, fbc_enabled = 0; - u32 tmp; - struct fbc_panel_info *fbc = &timing->fbc; - - fbc_enabled = of_property_read_bool(np, "qcom,mdss-dsi-fbc-enable"); - if (fbc_enabled) { - pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__); - fbc->enabled = 1; - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bpp", &tmp); - fbc->target_bpp = (!rc ? tmp : 24); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-packing", - &tmp); - fbc->comp_mode = (!rc ? tmp : 0); - fbc->qerr_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-quant-error"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bias", &tmp); - fbc->cd_bias = (!rc ? tmp : 0); - fbc->pat_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-pat-mode"); - fbc->vlc_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-vlc-mode"); - fbc->bflc_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-bflc-mode"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-h-line-budget", - &tmp); - fbc->line_x_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-budget-ctrl", - &tmp); - fbc->block_x_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-block-budget", - &tmp); - fbc->block_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossless-threshold", &tmp); - fbc->lossless_mode_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossy-threshold", &tmp); - fbc->lossy_mode_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-rgb-threshold", - &tmp); - fbc->lossy_rgb_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossy-mode-idx", &tmp); - fbc->lossy_mode_idx = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-slice-height", &tmp); - fbc->slice_height = (!rc ? tmp : 0); - fbc->pred_mode = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-2d-pred-mode"); - fbc->enc_mode = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-ver2-mode"); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-max-pred-err", &tmp); - fbc->max_pred_err = (!rc ? tmp : 0); - - timing->compression_mode = COMPRESSION_FBC; - } else { - pr_debug("%s:%d Panel does not support FBC.\n", - __func__, __LINE__); - fbc->enabled = 0; - fbc->target_bpp = 24; - } - return 0; -} - -void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_panel_info *pinfo) -{ - struct dsi_panel_cmds pcmds; - struct dsi_cmd_desc cmd; - - if (!pinfo || (pinfo->compression_mode != COMPRESSION_DSC)) - return; - - memset(&pcmds, 0, sizeof(pcmds)); - memset(&cmd, 0, sizeof(cmd)); - - cmd.dchdr.dlen = mdss_panel_dsc_prepare_pps_buf(&pinfo->dsc, - ctrl->pps_buf, 0); - cmd.dchdr.dtype = DTYPE_PPS; - cmd.dchdr.last = 1; - cmd.dchdr.wait = 10; - cmd.dchdr.vc = 0; - cmd.dchdr.ack = 0; - cmd.payload = ctrl->pps_buf; - - pcmds.cmd_cnt = 1; - pcmds.cmds = &cmd; - pcmds.link_state = DSI_LP_MODE; - - mdss_dsi_panel_cmds_send(ctrl, &pcmds, CMD_REQ_COMMIT); -} - -static int mdss_dsi_parse_hdr_settings(struct device_node *np, - struct mdss_panel_info *pinfo) -{ - int rc = 0; - struct mdss_panel_hdr_properties *hdr_prop; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - if (!pinfo) { - pr_err("%s: panel info is NULL\n", __func__); - return -EINVAL; - } - - hdr_prop = &pinfo->hdr_properties; - hdr_prop->hdr_enabled = of_property_read_bool(np, - "qcom,mdss-dsi-panel-hdr-enabled"); - - if (hdr_prop->hdr_enabled) { - rc = of_property_read_u32_array(np, - "qcom,mdss-dsi-panel-hdr-color-primaries", - hdr_prop->display_primaries, - DISPLAY_PRIMARIES_COUNT); - if (rc) { - pr_info("%s:%d, Unable to read color primaries,rc:%u", - __func__, __LINE__, - hdr_prop->hdr_enabled = false); - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-peak-brightness", - &(hdr_prop->peak_brightness)); - if (rc) { - pr_info("%s:%d, Unable to read hdr brightness, rc:%u", - __func__, __LINE__, rc); - hdr_prop->hdr_enabled = false; - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-blackness-level", - &(hdr_prop->blackness_level)); - if (rc) { - pr_info("%s:%d, Unable to read hdr brightness, rc:%u", - __func__, __LINE__, rc); - hdr_prop->hdr_enabled = false; - } - } - return 0; -} - -static int mdss_dsi_parse_split_link_settings(struct device_node *np, - struct mdss_panel_info *pinfo) -{ - u32 tmp; - int rc = 0; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - if (!pinfo) { - pr_err("%s: panel info is NULL\n", __func__); - return -EINVAL; - } - - pinfo->split_link_enabled = of_property_read_bool(np, - "qcom,split-link-enabled"); - - if (pinfo->split_link_enabled) { - rc = of_property_read_u32(np, - "qcom,sublinks-count", &tmp); - /* default num of sublink is 1*/ - pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); - - rc = of_property_read_u32(np, - "qcom,lanes-per-sublink", &tmp); - /* default num of lanes per sublink is 1 */ - pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); - } - - pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", - __func__, pinfo->split_link_enabled, - pinfo->mipi.num_of_sublinks, - pinfo->mipi.lanes_per_sublink); - return 0; -} - -static int mdss_dsi_parse_dsc_version(struct device_node *np, - struct mdss_panel_timing *timing) -{ - u32 data; - int rc = 0; - struct dsc_desc *dsc = &timing->dsc; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-version", &data); - if (rc) { - dsc->version = 0x11; - rc = 0; - } else { - dsc->version = data & 0xff; - /* only support DSC 1.1 rev */ - if (dsc->version != 0x11) { - pr_err("%s: DSC version:%d not supported\n", __func__, - dsc->version); - rc = -EINVAL; - goto end; - } - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-scr-version", &data); - if (rc) { - dsc->scr_rev = 0x0; - rc = 0; - } else { - dsc->scr_rev = data & 0xff; - /* only one scr rev supported */ - if (dsc->scr_rev > 0x1) { - pr_err("%s: DSC scr version:%d not supported\n", - __func__, dsc->scr_rev); - rc = -EINVAL; - goto end; - } - } - -end: - return rc; -} - -static int mdss_dsi_parse_dsc_params(struct device_node *np, - struct mdss_panel_timing *timing, bool is_split_display) -{ - u32 data, intf_width; - int rc = 0; - struct dsc_desc *dsc = &timing->dsc; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-encoders", &data); - if (rc) { - if (!of_find_property(np, "qcom,mdss-dsc-encoders", NULL)) { - /* property is not defined, default to 1 */ - data = 1; - } else { - pr_err("%s: Error parsing qcom,mdss-dsc-encoders\n", - __func__); - goto end; - } - } - - timing->dsc_enc_total = data; - - if (is_split_display && (timing->dsc_enc_total > 1)) { - pr_err("%s: Error: for split displays, more than 1 dsc encoder per panel is not allowed.\n", - __func__); - goto end; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-height", &data); - if (rc) - goto end; - dsc->slice_height = data; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-width", &data); - if (rc) - goto end; - dsc->slice_width = data; - intf_width = timing->xres; - - if (intf_width % dsc->slice_width) { - pr_err("%s: Error: multiple of slice-width:%d should match panel-width:%d\n", - __func__, dsc->slice_width, intf_width); - goto end; - } - - data = intf_width / dsc->slice_width; - if (((timing->dsc_enc_total > 1) && ((data != 2) && (data != 4))) || - ((timing->dsc_enc_total == 1) && (data > 2))) { - pr_err("%s: Error: max 2 slice per encoder. slice-width:%d should match panel-width:%d dsc_enc_total:%d\n", - __func__, dsc->slice_width, - intf_width, timing->dsc_enc_total); - goto end; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-per-pkt", &data); - if (rc) - goto end; - dsc->slice_per_pkt = data; - - /* - * slice_per_pkt can be either 1 or all slices_per_intf - */ - if ((dsc->slice_per_pkt > 1) && (dsc->slice_per_pkt != - DIV_ROUND_UP(intf_width, dsc->slice_width))) { - pr_err("Error: slice_per_pkt can be either 1 or all slices_per_intf\n"); - pr_err("%s: slice_per_pkt=%d, slice_width=%d intf_width=%d\n", - __func__, - dsc->slice_per_pkt, dsc->slice_width, intf_width); - rc = -EINVAL; - goto end; - } - - pr_debug("%s: num_enc:%d :slice h=%d w=%d s_pkt=%d\n", __func__, - timing->dsc_enc_total, dsc->slice_height, - dsc->slice_width, dsc->slice_per_pkt); - - rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-component", &data); - if (rc) - goto end; - dsc->bpc = data; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-pixel", &data); - if (rc) - goto end; - dsc->bpp = data; - - pr_debug("%s: bpc=%d bpp=%d\n", __func__, - dsc->bpc, dsc->bpp); - - dsc->block_pred_enable = of_property_read_bool(np, - "qcom,mdss-dsc-block-prediction-enable"); - - dsc->enable_422 = 0; - dsc->convert_rgb = 1; - dsc->vbr_enable = 0; - - dsc->config_by_manufacture_cmd = of_property_read_bool(np, - "qcom,mdss-dsc-config-by-manufacture-cmd"); - - mdss_panel_dsc_parameters_calc(&timing->dsc); - mdss_panel_dsc_pclk_param_calc(&timing->dsc, intf_width); - - timing->dsc.full_frame_slices = - DIV_ROUND_UP(intf_width, timing->dsc.slice_width); - - timing->compression_mode = COMPRESSION_DSC; - -end: - return rc; -} - -static struct device_node *mdss_dsi_panel_get_dsc_cfg_np( - struct device_node *np, struct mdss_panel_data *panel_data, - bool default_timing) -{ - struct device_node *dsc_cfg_np = NULL; - - - /* Read the dsc config node specified by command line */ - if (default_timing) { - dsc_cfg_np = of_get_child_by_name(np, - panel_data->dsc_cfg_np_name); - if (!dsc_cfg_np) - pr_warn_once("%s: cannot find dsc config node:%s\n", - __func__, panel_data->dsc_cfg_np_name); - } - - /* - * Fall back to default from DT as nothing is specified - * in command line. - */ - if (!dsc_cfg_np && of_find_property(np, "qcom,config-select", NULL)) { - dsc_cfg_np = of_parse_phandle(np, "qcom,config-select", 0); - if (!dsc_cfg_np) - pr_warn_once("%s:err parsing qcom,config-select\n", - __func__); - } - - return dsc_cfg_np; -} - -static int mdss_dsi_parse_topology_config(struct device_node *np, - struct dsi_panel_timing *pt, struct mdss_panel_data *panel_data, - bool default_timing) -{ - int rc = 0; - bool is_split_display = panel_data->panel_info.is_split_display; - const char *data; - struct mdss_panel_timing *timing = &pt->timing; - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo; - struct device_node *cfg_np = NULL; - - ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, - panel_data); - pinfo = &ctrl_pdata->panel_data.panel_info; - - cfg_np = mdss_dsi_panel_get_dsc_cfg_np(np, - &ctrl_pdata->panel_data, default_timing); - - if (cfg_np) { - if (!of_property_read_u32_array(cfg_np, "qcom,lm-split", - timing->lm_widths, 2)) { - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) - && (timing->lm_widths[1] != 0)) { - pr_err("%s: lm-split not allowed with split display\n", - __func__); - rc = -EINVAL; - goto end; - } - } - - if (!of_property_read_string(cfg_np, "qcom,split-mode", - &data) && !strcmp(data, "pingpong-split")) - pinfo->use_pingpong_split = true; - - if (((timing->lm_widths[0]) || (timing->lm_widths[1])) && - pinfo->use_pingpong_split) { - pr_err("%s: pingpong_split cannot be used when lm-split[%d,%d] is specified\n", - __func__, - timing->lm_widths[0], timing->lm_widths[1]); - return -EINVAL; - } - - pr_info("%s: cfg_node name %s lm_split:%dx%d pp_split:%s\n", - __func__, cfg_np->name, - timing->lm_widths[0], timing->lm_widths[1], - pinfo->use_pingpong_split ? "yes" : "no"); - } - - if (!pinfo->use_pingpong_split && - (timing->lm_widths[0] == 0) && (timing->lm_widths[1] == 0)) - timing->lm_widths[0] = pt->timing.xres; - - data = of_get_property(np, "qcom,compression-mode", NULL); - if (data) { - if (cfg_np && !strcmp(data, "dsc")) { - rc = mdss_dsi_parse_dsc_version(np, &pt->timing); - if (rc) - goto end; - - pinfo->send_pps_before_switch = - of_property_read_bool(np, - "qcom,mdss-dsi-send-pps-before-switch"); - - rc = mdss_dsi_parse_dsc_params(cfg_np, &pt->timing, - is_split_display); - } else if (!strcmp(data, "fbc")) { - rc = mdss_dsi_parse_fbc_params(np, &pt->timing); - } - } - -end: - of_node_put(cfg_np); - return rc; -} - -static void mdss_panel_parse_te_params(struct device_node *np, - struct mdss_panel_timing *timing) -{ - struct mdss_mdp_pp_tear_check *te = &timing->te; - u32 tmp; - int rc = 0; - /* - * TE default: dsi byte clock calculated base on 70 fps; - * around 14 ms to complete a kickoff cycle if te disabled; - * vclk_line base on 60 fps; write is faster than read; - * init == start == rdptr; - */ - te->tear_check_en = - !of_property_read_bool(np, "qcom,mdss-tear-check-disable"); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp); - te->sync_cfg_height = (!rc ? tmp : 0xfff0); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-init-val", &tmp); - te->vsync_init_val = (!rc ? tmp : timing->yres); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp); - te->sync_threshold_start = (!rc ? tmp : 4); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp); - te->sync_threshold_continue = (!rc ? tmp : 4); - rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp); - te->refx100 = (!rc ? tmp : 6000); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-start-pos", &tmp); - te->start_pos = (!rc ? tmp : timing->yres); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp); - te->rd_ptr_irq = (!rc ? tmp : timing->yres + 1); - te->wr_ptr_irq = 0; -} - - -static int mdss_dsi_parse_reset_seq(struct device_node *np, - u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, - const char *name) -{ - int num = 0, i; - int rc; - struct property *data; - u32 tmp[MDSS_DSI_RST_SEQ_LEN]; - *rst_len = 0; - data = of_find_property(np, name, &num); - num /= sizeof(u32); - if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) { - pr_debug("%s:%d, error reading %s, length found = %d\n", - __func__, __LINE__, name, num); - } else { - rc = of_property_read_u32_array(np, name, tmp, num); - if (rc) - pr_debug("%s:%d, error reading %s, rc = %d\n", - __func__, __LINE__, name, rc); - else { - for (i = 0; i < num; ++i) - rst_seq[i] = tmp[i]; - *rst_len = num; - } - } - return 0; -} - -static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl) -{ - int i, j = 0; - int len = 0, *lenp; - int group = 0; - - lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; - - for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++) - len += lenp[i]; - - for (i = 0; i < len; i++) { - pr_debug("[%i] return:0x%x status:0x%x\n", - i, (unsigned int)ctrl->return_buf[i], - (unsigned int)ctrl->status_value[j + i]); - MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i], - ctrl->status_value[j + i]); - j += len; - } - - for (j = 0; j < ctrl->groups; ++j) { - for (i = 0; i < len; ++i) { - if (ctrl->return_buf[i] != - ctrl->status_value[group + i]) - break; - } - - if (i == len) - return true; - group += len; - } - - return false; -} - -static int mdss_dsi_gen_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - if (!mdss_dsi_cmp_panel_reg_v2(ctrl_pdata)) { - pr_err("%s: Read back value from panel is incorrect\n", - __func__); - return -EINVAL; - } else { - return 1; - } -} - -static int mdss_dsi_nt35596_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 0)) { - ctrl_pdata->status_error_count = 0; - pr_err("%s: Read back value from panel is incorrect\n", - __func__); - return -EINVAL; - } else { - if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 3)) { - ctrl_pdata->status_error_count = 0; - } else { - if (mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 4) || - mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 5)) - ctrl_pdata->status_error_count = 0; - else - ctrl_pdata->status_error_count++; - if (ctrl_pdata->status_error_count >= - ctrl_pdata->max_status_error_count) { - ctrl_pdata->status_error_count = 0; - pr_err("%s: Read value bad. Error_cnt = %i\n", - __func__, - ctrl_pdata->status_error_count); - return -EINVAL; - } - } - return 1; - } -} - -static void mdss_dsi_parse_roi_alignment(struct device_node *np, - struct dsi_panel_timing *pt) -{ - int len = 0; - u32 value[6]; - struct property *data; - struct mdss_panel_timing *timing = &pt->timing; - - data = of_find_property(np, "qcom,panel-roi-alignment", &len); - len /= sizeof(u32); - if (!data || (len != 6)) { - pr_debug("%s: Panel roi alignment not found", __func__); - } else { - int rc = of_property_read_u32_array(np, - "qcom,panel-roi-alignment", value, len); - if (rc) - pr_debug("%s: Error reading panel roi alignment values", - __func__); - else { - timing->roi_alignment.xstart_pix_align = value[0]; - timing->roi_alignment.ystart_pix_align = value[1]; - timing->roi_alignment.width_pix_align = value[2]; - timing->roi_alignment.height_pix_align = value[3]; - timing->roi_alignment.min_width = value[4]; - timing->roi_alignment.min_height = value[5]; - } - - pr_debug("%s: ROI alignment: [%d, %d, %d, %d, %d, %d]", - __func__, timing->roi_alignment.xstart_pix_align, - timing->roi_alignment.width_pix_align, - timing->roi_alignment.ystart_pix_align, - timing->roi_alignment.height_pix_align, - timing->roi_alignment.min_width, - timing->roi_alignment.min_height); - } -} - -static void mdss_dsi_parse_dms_config(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) -{ - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - const char *data; - bool dms_enabled; - - dms_enabled = of_property_read_bool(np, - "qcom,dynamic-mode-switch-enabled"); - - if (!dms_enabled) { - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - goto exit; - } - - /* default mode is suspend_resume */ - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_SUSPEND_RESUME; - data = of_get_property(np, "qcom,dynamic-mode-switch-type", NULL); - if (data && !strcmp(data, "dynamic-resolution-switch-immediate")) { - if (!list_empty(&ctrl->panel_data.timings_list)) - pinfo->mipi.dms_mode = - DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE; - else - pinfo->mipi.dms_mode = - DYNAMIC_MODE_SWITCH_DISABLED; - goto exit; - } - - if (data && !strcmp(data, "dynamic-switch-immediate")) - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_IMMEDIATE; - else - pr_debug("%s: default dms suspend/resume\n", __func__); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd, - "qcom,video-to-cmd-mode-switch-commands", - "qcom,mode-switch-commands-state"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video, - "qcom,cmd-to-video-mode-switch-commands", - "qcom,mode-switch-commands-state"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->post_dms_on_cmds, - "qcom,mdss-dsi-post-mode-switch-on-command", - "qcom,mdss-dsi-post-mode-switch-on-command-state"); - - if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE && - !ctrl->post_dms_on_cmds.cmd_cnt) { - pr_warn("%s: No post dms on cmd specified\n", __func__); - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - } - - if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) { - pr_warn("%s: No commands specified for dynamic switch\n", - __func__); - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - } -exit: - pr_info("%s: dynamic switch feature enabled: %d\n", __func__, - pinfo->mipi.dms_mode); - return; -} - -/* the length of all the valid values to be checked should not be great - * than the length of returned data from read command. - */ -static bool -mdss_dsi_parse_esd_check_valid_params(struct mdss_dsi_ctrl_pdata *ctrl) -{ - int i; - - for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) { - if (ctrl->status_valid_params[i] > ctrl->status_cmds_rlen[i]) { - pr_debug("%s: ignore valid params!\n", __func__); - return false; - } - } - - return true; -} - -static bool mdss_dsi_parse_esd_status_len(struct device_node *np, - char *prop_key, u32 **target, u32 cmd_cnt) -{ - int tmp; - - if (!of_find_property(np, prop_key, &tmp)) - return false; - - tmp /= sizeof(u32); - if (tmp != cmd_cnt) { - pr_err("%s: request property number(%d) not match command count(%d)\n", - __func__, tmp, cmd_cnt); - return false; - } - - *target = kcalloc(tmp, sizeof(u32), GFP_KERNEL); - if (IS_ERR_OR_NULL(*target)) { - pr_err("%s: Error allocating memory for property\n", - __func__); - return false; - } - - if (of_property_read_u32_array(np, prop_key, *target, tmp)) { - pr_err("%s: cannot get values from dts\n", __func__); - kfree(*target); - *target = NULL; - return false; - } - - return true; -} - -static void mdss_dsi_parse_esd_params(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) -{ - u32 tmp; - u32 i, status_len, *lenp; - int rc; - struct property *data; - const char *string; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - - pinfo->esd_check_enabled = of_property_read_bool(np, - "qcom,esd-check-enabled"); - - if (!pinfo->esd_check_enabled) - return; - - ctrl->status_mode = ESD_MAX; - rc = of_property_read_string(np, - "qcom,mdss-dsi-panel-status-check-mode", &string); - if (!rc) { - if (!strcmp(string, "bta_check")) { - ctrl->status_mode = ESD_BTA; - } else if (!strcmp(string, "reg_read")) { - ctrl->status_mode = ESD_REG; - ctrl->check_read_status = - mdss_dsi_gen_read_status; - } else if (!strcmp(string, "reg_read_nt35596")) { - ctrl->status_mode = ESD_REG_NT35596; - ctrl->status_error_count = 0; - ctrl->check_read_status = - mdss_dsi_nt35596_read_status; - } else if (!strcmp(string, "te_signal_check")) { - if (pinfo->mipi.mode == DSI_CMD_MODE) { - ctrl->status_mode = ESD_TE; - } else { - pr_err("TE-ESD not valid for video mode\n"); - goto error; - } - } else { - pr_err("No valid panel-status-check-mode string\n"); - goto error; - } - } - - if ((ctrl->status_mode == ESD_BTA) || (ctrl->status_mode == ESD_TE) || - (ctrl->status_mode == ESD_MAX)) - return; - - mdss_dsi_parse_dcs_cmds(np, &ctrl->status_cmds, - "qcom,mdss-dsi-panel-status-command", - "qcom,mdss-dsi-panel-status-command-state"); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-max-error-count", - &tmp); - ctrl->max_status_error_count = (!rc ? tmp : 0); - - if (!mdss_dsi_parse_esd_status_len(np, - "qcom,mdss-dsi-panel-status-read-length", - &ctrl->status_cmds_rlen, ctrl->status_cmds.cmd_cnt)) { - pinfo->esd_check_enabled = false; - return; - } - - if (mdss_dsi_parse_esd_status_len(np, - "qcom,mdss-dsi-panel-status-valid-params", - &ctrl->status_valid_params, ctrl->status_cmds.cmd_cnt)) { - if (!mdss_dsi_parse_esd_check_valid_params(ctrl)) - goto error1; - } - - status_len = 0; - lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; - for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) - status_len += lenp[i]; - - data = of_find_property(np, "qcom,mdss-dsi-panel-status-value", &tmp); - tmp /= sizeof(u32); - if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) { - ctrl->groups = tmp / status_len; - } else { - pr_err("%s: Error parse panel-status-value\n", __func__); - goto error1; - } - - ctrl->status_value = kzalloc(sizeof(u32) * status_len * ctrl->groups, - GFP_KERNEL); - if (!ctrl->status_value) - goto error1; - - ctrl->return_buf = kcalloc(status_len * ctrl->groups, - sizeof(unsigned char), GFP_KERNEL); - if (!ctrl->return_buf) - goto error2; - - rc = of_property_read_u32_array(np, - "qcom,mdss-dsi-panel-status-value", - ctrl->status_value, ctrl->groups * status_len); - if (rc) { - pr_debug("%s: Error reading panel status values\n", - __func__); - memset(ctrl->status_value, 0, ctrl->groups * status_len); - } - - return; - -error2: - kfree(ctrl->status_value); -error1: - kfree(ctrl->status_valid_params); - kfree(ctrl->status_cmds_rlen); -error: - pinfo->esd_check_enabled = false; -} - -static void mdss_dsi_parse_partial_update_caps(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) -{ - struct mdss_panel_info *pinfo; - const char *data; - - pinfo = &ctrl->panel_data.panel_info; - - data = of_get_property(np, "qcom,partial-update-enabled", NULL); - if (data && !strcmp(data, "single_roi")) - pinfo->partial_update_supported = - PU_SINGLE_ROI; - else if (data && !strcmp(data, "dual_roi")) - pinfo->partial_update_supported = - PU_DUAL_ROI; - else if (data && !strcmp(data, "none")) - pinfo->partial_update_supported = - PU_NOT_SUPPORTED; - else - pinfo->partial_update_supported = - PU_NOT_SUPPORTED; - - if (pinfo->mipi.mode == DSI_CMD_MODE) { - pinfo->partial_update_enabled = pinfo->partial_update_supported; - pr_info("%s: partial_update_enabled=%d\n", __func__, - pinfo->partial_update_enabled); - ctrl->set_col_page_addr = mdss_dsi_set_col_page_addr; - if (pinfo->partial_update_enabled) { - pinfo->partial_update_roi_merge = - of_property_read_bool(np, - "qcom,partial-update-roi-merge"); - } - } -} - -static int mdss_dsi_parse_panel_features(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) -{ - struct mdss_panel_info *pinfo; - - if (!np || !ctrl) { - pr_err("%s: Invalid arguments\n", __func__); - return -ENODEV; - } - - pinfo = &ctrl->panel_data.panel_info; - - mdss_dsi_parse_partial_update_caps(np, ctrl); - - pinfo->dcs_cmd_by_left = of_property_read_bool(np, - "qcom,dcs-cmd-by-left"); - - pinfo->ulps_feature_enabled = of_property_read_bool(np, - "qcom,ulps-enabled"); - pr_info("%s: ulps feature %s\n", __func__, - (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); - - pinfo->ulps_suspend_enabled = of_property_read_bool(np, - "qcom,suspend-ulps-enabled"); - pr_info("%s: ulps during suspend feature %s", __func__, - (pinfo->ulps_suspend_enabled ? "enabled" : "disabled")); - - mdss_dsi_parse_dms_config(np, ctrl); - - pinfo->panel_ack_disabled = pinfo->sim_panel_mode ? - 1 : of_property_read_bool(np, "qcom,panel-ack-disabled"); - - pinfo->allow_phy_power_off = of_property_read_bool(np, - "qcom,panel-allow-phy-poweroff"); - - mdss_dsi_parse_esd_params(np, ctrl); - - if (pinfo->panel_ack_disabled && pinfo->esd_check_enabled) { - pr_warn("ESD should not be enabled if panel ACK is disabled\n"); - pinfo->esd_check_enabled = false; - } - - if (ctrl->disp_en_gpio <= 0) { - ctrl->disp_en_gpio = of_get_named_gpio( - np, - "qcom,5v-boost-gpio", 0); - - if (!gpio_is_valid(ctrl->disp_en_gpio)) - pr_debug("%s:%d, Disp_en gpio not specified\n", - __func__, __LINE__); - } - - mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, - "qcom,mdss-dsi-lp-mode-on", NULL); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, - "qcom,mdss-dsi-lp-mode-off", NULL); - - return 0; -} - -static void mdss_dsi_parse_panel_horizintal_line_idle(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) -{ - const u32 *src; - int i, len, cnt; - struct panel_horizontal_idle *kp; - - if (!np || !ctrl) { - pr_err("%s: Invalid arguments\n", __func__); - return; - } - - src = of_get_property(np, "qcom,mdss-dsi-hor-line-idle", &len); - if (!src || len == 0) - return; - - cnt = len % 3; /* 3 fields per entry */ - if (cnt) { - pr_err("%s: invalid horizontal idle len=%d\n", __func__, len); - return; - } - - cnt = len / sizeof(u32); - - kp = kzalloc(sizeof(*kp) * (cnt / 3), GFP_KERNEL); - if (kp == NULL) { - pr_err("%s: No memory\n", __func__); - return; - } - - ctrl->line_idle = kp; - for (i = 0; i < cnt; i += 3) { - kp->min = be32_to_cpu(src[i]); - kp->max = be32_to_cpu(src[i+1]); - kp->idle = be32_to_cpu(src[i+2]); - kp++; - ctrl->horizontal_idle_cnt++; - } - - /* - * idle is enabled for this controller, this will be used to - * enable/disable burst mode since both features are mutually - * exclusive. - */ - ctrl->idle_enabled = true; - - pr_debug("%s: horizontal_idle_cnt=%d\n", __func__, - ctrl->horizontal_idle_cnt); -} - -static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node, - struct mdss_panel_info *pinfo) -{ - int rc = 0; - rc = of_property_read_u32(pan_node, - "qcom,mdss-dsi-min-refresh-rate", - &pinfo->min_fps); - if (rc) { - pr_warn("%s:%d, Unable to read min refresh rate\n", - __func__, __LINE__); - - /* - * If min refresh rate is not specified, set it to the - * default panel refresh rate. - */ - pinfo->min_fps = pinfo->mipi.frame_rate; - rc = 0; - } - - rc = of_property_read_u32(pan_node, - "qcom,mdss-dsi-max-refresh-rate", - &pinfo->max_fps); - if (rc) { - pr_warn("%s:%d, Unable to read max refresh rate\n", - __func__, __LINE__); - - /* - * Since max refresh rate was not specified when dynamic - * fps is enabled, using the default panel refresh rate - * as max refresh rate supported. - */ - pinfo->max_fps = pinfo->mipi.frame_rate; - rc = 0; - } - - pr_info("dyn_fps: min = %d, max = %d\n", - pinfo->min_fps, pinfo->max_fps); - return rc; -} - -static void mdss_dsi_parse_dfps_config(struct device_node *pan_node, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - const char *data; - bool dynamic_fps; - struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); - - dynamic_fps = of_property_read_bool(pan_node, - "qcom,mdss-dsi-pan-enable-dynamic-fps"); - - if (!dynamic_fps) - return; - - pinfo->dynamic_fps = true; - data = of_get_property(pan_node, "qcom,mdss-dsi-pan-fps-update", NULL); - if (data) { - if (!strcmp(data, "dfps_suspend_resume_mode")) { - pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; - pr_debug("dfps mode: suspend/resume\n"); - } else if (!strcmp(data, "dfps_immediate_clk_mode")) { - pinfo->dfps_update = DFPS_IMMEDIATE_CLK_UPDATE_MODE; - pr_debug("dfps mode: Immediate clk\n"); - } else if (!strcmp(data, "dfps_immediate_porch_mode_hfp")) { - pinfo->dfps_update = - DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP; - pr_debug("dfps mode: Immediate porch HFP\n"); - } else if (!strcmp(data, "dfps_immediate_porch_mode_vfp")) { - pinfo->dfps_update = - DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP; - pr_debug("dfps mode: Immediate porch VFP\n"); - } else { - pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; - pr_debug("default dfps mode: suspend/resume\n"); - } - } else { - pinfo->dynamic_fps = false; - pr_debug("dfps update mode not configured: disable\n"); - } - pinfo->new_fps = pinfo->mipi.frame_rate; - pinfo->current_fps = pinfo->mipi.frame_rate; - - return; -} - -int mdss_panel_parse_bl_settings(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - const char *data; - int rc = 0; - u32 tmp; - - ctrl_pdata->bklt_ctrl = UNKNOWN_CTRL; - data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL); - if (data) { - if (!strcmp(data, "bl_ctrl_wled")) { - led_trigger_register_simple("bkl-trigger", - &bl_led_trigger); - pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", - __func__); - ctrl_pdata->bklt_ctrl = BL_WLED; - } else if (!strcmp(data, "bl_ctrl_pwm")) { - ctrl_pdata->bklt_ctrl = BL_PWM; - ctrl_pdata->pwm_pmi = of_property_read_bool(np, - "qcom,mdss-dsi-bl-pwm-pmi"); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-bl-pmic-pwm-frequency", &tmp); - if (rc) { - pr_err("%s:%d, Error, panel pwm_period\n", - __func__, __LINE__); - return -EINVAL; - } - ctrl_pdata->pwm_period = tmp; - if (ctrl_pdata->pwm_pmi) { - ctrl_pdata->pwm_bl = of_pwm_get(np, NULL); - if (IS_ERR(ctrl_pdata->pwm_bl)) { - pr_err("%s: Error, pwm device\n", - __func__); - ctrl_pdata->pwm_bl = NULL; - return -EINVAL; - } - } else { - rc = of_property_read_u32(np, - "qcom,mdss-dsi-bl-pmic-bank-select", - &tmp); - if (rc) { - pr_err("%s:%d, Error, lpg channel\n", - __func__, __LINE__); - return -EINVAL; - } - ctrl_pdata->pwm_lpg_chan = tmp; - tmp = of_get_named_gpio(np, - "qcom,mdss-dsi-pwm-gpio", 0); - ctrl_pdata->pwm_pmic_gpio = tmp; - pr_debug("%s: Configured PWM bklt ctrl\n", + + static int mdss_dsi_parse_fbc_params(struct device_node *np, + struct mdss_panel_timing *timing) + { + int rc, fbc_enabled = 0; + u32 tmp; + struct fbc_panel_info *fbc = &timing->fbc; + + fbc_enabled = of_property_read_bool(np, "qcom,mdss-dsi-fbc-enable"); + if (fbc_enabled) { + pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__); + fbc->enabled = 1; + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bpp", &tmp); + fbc->target_bpp = (!rc ? tmp : 24); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-packing", + &tmp); + fbc->comp_mode = (!rc ? tmp : 0); + fbc->qerr_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-quant-error"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bias", &tmp); + fbc->cd_bias = (!rc ? tmp : 0); + fbc->pat_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-pat-mode"); + fbc->vlc_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-vlc-mode"); + fbc->bflc_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-bflc-mode"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-h-line-budget", + &tmp); + fbc->line_x_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-budget-ctrl", + &tmp); + fbc->block_x_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-block-budget", + &tmp); + fbc->block_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossless-threshold", &tmp); + fbc->lossless_mode_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossy-threshold", &tmp); + fbc->lossy_mode_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-rgb-threshold", + &tmp); + fbc->lossy_rgb_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossy-mode-idx", &tmp); + fbc->lossy_mode_idx = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-slice-height", &tmp); + fbc->slice_height = (!rc ? tmp : 0); + fbc->pred_mode = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-2d-pred-mode"); + fbc->enc_mode = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-ver2-mode"); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-max-pred-err", &tmp); + fbc->max_pred_err = (!rc ? tmp : 0); + + timing->compression_mode = COMPRESSION_FBC; + } else { + pr_debug("%s:%d Panel does not support FBC.\n", + __func__, __LINE__); + fbc->enabled = 0; + fbc->target_bpp = 24; + } + return 0; + } + + void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_info *pinfo) + { + struct dsi_panel_cmds pcmds; + struct dsi_cmd_desc cmd; + + if (!pinfo || (pinfo->compression_mode != COMPRESSION_DSC)) + return; + + memset(&pcmds, 0, sizeof(pcmds)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.dchdr.dlen = mdss_panel_dsc_prepare_pps_buf(&pinfo->dsc, + ctrl->pps_buf, 0); + cmd.dchdr.dtype = DTYPE_PPS; + cmd.dchdr.last = 1; + cmd.dchdr.wait = 10; + cmd.dchdr.vc = 0; + cmd.dchdr.ack = 0; + cmd.payload = ctrl->pps_buf; + + pcmds.cmd_cnt = 1; + pcmds.cmds = &cmd; + pcmds.link_state = DSI_LP_MODE; + + mdss_dsi_panel_cmds_send(ctrl, &pcmds, CMD_REQ_COMMIT); + } + + static int mdss_dsi_parse_hdr_settings(struct device_node *np, + struct mdss_panel_info *pinfo) + { + int rc = 0; + struct mdss_panel_hdr_properties *hdr_prop; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + hdr_prop = &pinfo->hdr_properties; + hdr_prop->hdr_enabled = of_property_read_bool(np, + "qcom,mdss-dsi-panel-hdr-enabled"); + + if (hdr_prop->hdr_enabled) { + rc = of_property_read_u32_array(np, + "qcom,mdss-dsi-panel-hdr-color-primaries", + hdr_prop->display_primaries, + DISPLAY_PRIMARIES_COUNT); + if (rc) { + pr_info("%s:%d, Unable to read color primaries,rc:%u", + __func__, __LINE__, + hdr_prop->hdr_enabled = false); + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-peak-brightness", + &(hdr_prop->peak_brightness)); + if (rc) { + pr_info("%s:%d, Unable to read hdr brightness, rc:%u", + __func__, __LINE__, rc); + hdr_prop->hdr_enabled = false; + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-blackness-level", + &(hdr_prop->blackness_level)); + if (rc) { + pr_info("%s:%d, Unable to read hdr brightness, rc:%u", + __func__, __LINE__, rc); + hdr_prop->hdr_enabled = false; + } + } + return 0; + } + + static int mdss_dsi_parse_split_link_settings(struct device_node *np, + struct mdss_panel_info *pinfo) + { + u32 tmp; + int rc = 0; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + pinfo->split_link_enabled = of_property_read_bool(np, + "qcom,split-link-enabled"); + + if (pinfo->split_link_enabled) { + rc = of_property_read_u32(np, + "qcom,sublinks-count", &tmp); + /* default num of sublink is 1*/ + pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); + + rc = of_property_read_u32(np, + "qcom,lanes-per-sublink", &tmp); + /* default num of lanes per sublink is 1 */ + pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); + } + + pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", + __func__, pinfo->split_link_enabled, + pinfo->mipi.num_of_sublinks, + pinfo->mipi.lanes_per_sublink); + return 0; + } + + static int mdss_dsi_parse_dsc_version(struct device_node *np, + struct mdss_panel_timing *timing) + { + u32 data; + int rc = 0; + struct dsc_desc *dsc = &timing->dsc; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-version", &data); + if (rc) { + dsc->version = 0x11; + rc = 0; + } else { + dsc->version = data & 0xff; + /* only support DSC 1.1 rev */ + if (dsc->version != 0x11) { + pr_err("%s: DSC version:%d not supported\n", __func__, + dsc->version); + rc = -EINVAL; + goto end; + } + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-scr-version", &data); + if (rc) { + dsc->scr_rev = 0x0; + rc = 0; + } else { + dsc->scr_rev = data & 0xff; + /* only one scr rev supported */ + if (dsc->scr_rev > 0x1) { + pr_err("%s: DSC scr version:%d not supported\n", + __func__, dsc->scr_rev); + rc = -EINVAL; + goto end; + } + } + + end: + return rc; + } + + static int mdss_dsi_parse_dsc_params(struct device_node *np, + struct mdss_panel_timing *timing, bool is_split_display) + { + u32 data, intf_width; + int rc = 0; + struct dsc_desc *dsc = &timing->dsc; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-encoders", &data); + if (rc) { + if (!of_find_property(np, "qcom,mdss-dsc-encoders", NULL)) { + /* property is not defined, default to 1 */ + data = 1; + } else { + pr_err("%s: Error parsing qcom,mdss-dsc-encoders\n", + __func__); + goto end; + } + } + + timing->dsc_enc_total = data; + + if (is_split_display && (timing->dsc_enc_total > 1)) { + pr_err("%s: Error: for split displays, more than 1 dsc encoder per panel is not allowed.\n", + __func__); + goto end; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-height", &data); + if (rc) + goto end; + dsc->slice_height = data; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-width", &data); + if (rc) + goto end; + dsc->slice_width = data; + intf_width = timing->xres; + + if (intf_width % dsc->slice_width) { + pr_err("%s: Error: multiple of slice-width:%d should match panel-width:%d\n", + __func__, dsc->slice_width, intf_width); + goto end; + } + + data = intf_width / dsc->slice_width; + if (((timing->dsc_enc_total > 1) && ((data != 2) && (data != 4))) || + ((timing->dsc_enc_total == 1) && (data > 2))) { + pr_err("%s: Error: max 2 slice per encoder. slice-width:%d should match panel-width:%d dsc_enc_total:%d\n", + __func__, dsc->slice_width, + intf_width, timing->dsc_enc_total); + goto end; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-per-pkt", &data); + if (rc) + goto end; + dsc->slice_per_pkt = data; + + /* + * slice_per_pkt can be either 1 or all slices_per_intf + */ + if ((dsc->slice_per_pkt > 1) && (dsc->slice_per_pkt != + DIV_ROUND_UP(intf_width, dsc->slice_width))) { + pr_err("Error: slice_per_pkt can be either 1 or all slices_per_intf\n"); + pr_err("%s: slice_per_pkt=%d, slice_width=%d intf_width=%d\n", + __func__, + dsc->slice_per_pkt, dsc->slice_width, intf_width); + rc = -EINVAL; + goto end; + } + + pr_debug("%s: num_enc:%d :slice h=%d w=%d s_pkt=%d\n", __func__, + timing->dsc_enc_total, dsc->slice_height, + dsc->slice_width, dsc->slice_per_pkt); + + rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-component", &data); + if (rc) + goto end; + dsc->bpc = data; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-pixel", &data); + if (rc) + goto end; + dsc->bpp = data; + + pr_debug("%s: bpc=%d bpp=%d\n", __func__, + dsc->bpc, dsc->bpp); + + dsc->block_pred_enable = of_property_read_bool(np, + "qcom,mdss-dsc-block-prediction-enable"); + + dsc->enable_422 = 0; + dsc->convert_rgb = 1; + dsc->vbr_enable = 0; + + dsc->config_by_manufacture_cmd = of_property_read_bool(np, + "qcom,mdss-dsc-config-by-manufacture-cmd"); + + mdss_panel_dsc_parameters_calc(&timing->dsc); + mdss_panel_dsc_pclk_param_calc(&timing->dsc, intf_width); + + timing->dsc.full_frame_slices = + DIV_ROUND_UP(intf_width, timing->dsc.slice_width); + + timing->compression_mode = COMPRESSION_DSC; + + end: + return rc; + } + + static struct device_node *mdss_dsi_panel_get_dsc_cfg_np( + struct device_node *np, struct mdss_panel_data *panel_data, + bool default_timing) + { + struct device_node *dsc_cfg_np = NULL; + + + /* Read the dsc config node specified by command line */ + if (default_timing) { + dsc_cfg_np = of_get_child_by_name(np, + panel_data->dsc_cfg_np_name); + if (!dsc_cfg_np) + pr_warn_once("%s: cannot find dsc config node:%s\n", + __func__, panel_data->dsc_cfg_np_name); + } + + /* + * Fall back to default from DT as nothing is specified + * in command line. + */ + if (!dsc_cfg_np && of_find_property(np, "qcom,config-select", NULL)) { + dsc_cfg_np = of_parse_phandle(np, "qcom,config-select", 0); + if (!dsc_cfg_np) + pr_warn_once("%s:err parsing qcom,config-select\n", + __func__); + } + + return dsc_cfg_np; + } + + static int mdss_dsi_parse_topology_config(struct device_node *np, + struct dsi_panel_timing *pt, struct mdss_panel_data *panel_data, + bool default_timing) + { + int rc = 0; + bool is_split_display = panel_data->panel_info.is_split_display; + const char *data; + struct mdss_panel_timing *timing = &pt->timing; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo; + struct device_node *cfg_np = NULL; + + ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, + panel_data); + pinfo = &ctrl_pdata->panel_data.panel_info; + + cfg_np = mdss_dsi_panel_get_dsc_cfg_np(np, + &ctrl_pdata->panel_data, default_timing); + + if (cfg_np) { + if (!of_property_read_u32_array(cfg_np, "qcom,lm-split", + timing->lm_widths, 2)) { + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) + && (timing->lm_widths[1] != 0)) { + pr_err("%s: lm-split not allowed with split display\n", + __func__); + rc = -EINVAL; + goto end; + } + } + + if (!of_property_read_string(cfg_np, "qcom,split-mode", + &data) && !strcmp(data, "pingpong-split")) + pinfo->use_pingpong_split = true; + + if (((timing->lm_widths[0]) || (timing->lm_widths[1])) && + pinfo->use_pingpong_split) { + pr_err("%s: pingpong_split cannot be used when lm-split[%d,%d] is specified\n", + __func__, + timing->lm_widths[0], timing->lm_widths[1]); + return -EINVAL; + } + + pr_info("%s: cfg_node name %s lm_split:%dx%d pp_split:%s\n", + __func__, cfg_np->name, + timing->lm_widths[0], timing->lm_widths[1], + pinfo->use_pingpong_split ? "yes" : "no"); + } + + if (!pinfo->use_pingpong_split && + (timing->lm_widths[0] == 0) && (timing->lm_widths[1] == 0)) + timing->lm_widths[0] = pt->timing.xres; + + data = of_get_property(np, "qcom,compression-mode", NULL); + if (data) { + if (cfg_np && !strcmp(data, "dsc")) { + rc = mdss_dsi_parse_dsc_version(np, &pt->timing); + if (rc) + goto end; + + pinfo->send_pps_before_switch = + of_property_read_bool(np, + "qcom,mdss-dsi-send-pps-before-switch"); + + rc = mdss_dsi_parse_dsc_params(cfg_np, &pt->timing, + is_split_display); + } else if (!strcmp(data, "fbc")) { + rc = mdss_dsi_parse_fbc_params(np, &pt->timing); + } + } + + end: + of_node_put(cfg_np); + return rc; + } + + static void mdss_panel_parse_te_params(struct device_node *np, + struct mdss_panel_timing *timing) + { + struct mdss_mdp_pp_tear_check *te = &timing->te; + u32 tmp; + int rc = 0; + /* + * TE default: dsi byte clock calculated base on 70 fps; + * around 14 ms to complete a kickoff cycle if te disabled; + * vclk_line base on 60 fps; write is faster than read; + * init == start == rdptr; + */ + te->tear_check_en = + !of_property_read_bool(np, "qcom,mdss-tear-check-disable"); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp); + te->sync_cfg_height = (!rc ? tmp : 0xfff0); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-init-val", &tmp); + te->vsync_init_val = (!rc ? tmp : timing->yres); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp); + te->sync_threshold_start = (!rc ? tmp : 4); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp); + te->sync_threshold_continue = (!rc ? tmp : 4); + rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp); + te->refx100 = (!rc ? tmp : 6000); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-start-pos", &tmp); + te->start_pos = (!rc ? tmp : timing->yres); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp); + te->rd_ptr_irq = (!rc ? tmp : timing->yres + 1); + te->wr_ptr_irq = 0; + } + + + static int mdss_dsi_parse_reset_seq(struct device_node *np, + u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, + const char *name) + { + int num = 0, i; + int rc; + struct property *data; + u32 tmp[MDSS_DSI_RST_SEQ_LEN]; + *rst_len = 0; + data = of_find_property(np, name, &num); + num /= sizeof(u32); + if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) { + pr_debug("%s:%d, error reading %s, length found = %d\n", + __func__, __LINE__, name, num); + } else { + rc = of_property_read_u32_array(np, name, tmp, num); + if (rc) + pr_debug("%s:%d, error reading %s, rc = %d\n", + __func__, __LINE__, name, rc); + else { + for (i = 0; i < num; ++i) + rst_seq[i] = tmp[i]; + *rst_len = num; + } + } + return 0; + } + + static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl) + { + int i, j = 0; + int len = 0, *lenp; + int group = 0; + + lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; + + for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++) + len += lenp[i]; + + for (i = 0; i < len; i++) { + pr_debug("[%i] return:0x%x status:0x%x\n", + i, (unsigned int)ctrl->return_buf[i], + (unsigned int)ctrl->status_value[j + i]); + MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i], + ctrl->status_value[j + i]); + j += len; + } + + for (j = 0; j < ctrl->groups; ++j) { + for (i = 0; i < len; ++i) { + if (ctrl->return_buf[i] != + ctrl->status_value[group + i]) + break; + } + + if (i == len) + return true; + group += len; + } + + return false; + } + + static int mdss_dsi_gen_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + if (!mdss_dsi_cmp_panel_reg_v2(ctrl_pdata)) { + pr_err("%s: Read back value from panel is incorrect\n", + __func__); + return -EINVAL; + } else { + return 1; + } + } + + static int mdss_dsi_nt35596_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 0)) { + ctrl_pdata->status_error_count = 0; + pr_err("%s: Read back value from panel is incorrect\n", + __func__); + return -EINVAL; + } else { + if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 3)) { + ctrl_pdata->status_error_count = 0; + } else { + if (mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 4) || + mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 5)) + ctrl_pdata->status_error_count = 0; + else + ctrl_pdata->status_error_count++; + if (ctrl_pdata->status_error_count >= + ctrl_pdata->max_status_error_count) { + ctrl_pdata->status_error_count = 0; + pr_err("%s: Read value bad. Error_cnt = %i\n", + __func__, + ctrl_pdata->status_error_count); + return -EINVAL; + } + } + return 1; + } + } + + static void mdss_dsi_parse_roi_alignment(struct device_node *np, + struct dsi_panel_timing *pt) + { + int len = 0; + u32 value[6]; + struct property *data; + struct mdss_panel_timing *timing = &pt->timing; + + data = of_find_property(np, "qcom,panel-roi-alignment", &len); + len /= sizeof(u32); + if (!data || (len != 6)) { + pr_debug("%s: Panel roi alignment not found", __func__); + } else { + int rc = of_property_read_u32_array(np, + "qcom,panel-roi-alignment", value, len); + if (rc) + pr_debug("%s: Error reading panel roi alignment values", + __func__); + else { + timing->roi_alignment.xstart_pix_align = value[0]; + timing->roi_alignment.ystart_pix_align = value[1]; + timing->roi_alignment.width_pix_align = value[2]; + timing->roi_alignment.height_pix_align = value[3]; + timing->roi_alignment.min_width = value[4]; + timing->roi_alignment.min_height = value[5]; + } + + pr_debug("%s: ROI alignment: [%d, %d, %d, %d, %d, %d]", + __func__, timing->roi_alignment.xstart_pix_align, + timing->roi_alignment.width_pix_align, + timing->roi_alignment.ystart_pix_align, + timing->roi_alignment.height_pix_align, + timing->roi_alignment.min_width, + timing->roi_alignment.min_height); + } + } + + static void mdss_dsi_parse_dms_config(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) + { + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + const char *data; + bool dms_enabled; + + dms_enabled = of_property_read_bool(np, + "qcom,dynamic-mode-switch-enabled"); + + if (!dms_enabled) { + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + goto exit; + } + + /* default mode is suspend_resume */ + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_SUSPEND_RESUME; + data = of_get_property(np, "qcom,dynamic-mode-switch-type", NULL); + if (data && !strcmp(data, "dynamic-resolution-switch-immediate")) { + if (!list_empty(&ctrl->panel_data.timings_list)) + pinfo->mipi.dms_mode = + DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE; + else + pinfo->mipi.dms_mode = + DYNAMIC_MODE_SWITCH_DISABLED; + goto exit; + } + + if (data && !strcmp(data, "dynamic-switch-immediate")) + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_IMMEDIATE; + else + pr_debug("%s: default dms suspend/resume\n", __func__); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd, + "qcom,video-to-cmd-mode-switch-commands", + "qcom,mode-switch-commands-state"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video, + "qcom,cmd-to-video-mode-switch-commands", + "qcom,mode-switch-commands-state"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->post_dms_on_cmds, + "qcom,mdss-dsi-post-mode-switch-on-command", + "qcom,mdss-dsi-post-mode-switch-on-command-state"); + + if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE && + !ctrl->post_dms_on_cmds.cmd_cnt) { + pr_warn("%s: No post dms on cmd specified\n", __func__); + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + } + + if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) { + pr_warn("%s: No commands specified for dynamic switch\n", + __func__); + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + } + exit: + pr_info("%s: dynamic switch feature enabled: %d\n", __func__, + pinfo->mipi.dms_mode); + return; + } + + /* the length of all the valid values to be checked should not be great + * than the length of returned data from read command. + */ + static bool + mdss_dsi_parse_esd_check_valid_params(struct mdss_dsi_ctrl_pdata *ctrl) + { + int i; + + for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) { + if (ctrl->status_valid_params[i] > ctrl->status_cmds_rlen[i]) { + pr_debug("%s: ignore valid params!\n", __func__); + return false; + } + } + + return true; + } + + static bool mdss_dsi_parse_esd_status_len(struct device_node *np, + char *prop_key, u32 **target, u32 cmd_cnt) + { + int tmp; + + if (!of_find_property(np, prop_key, &tmp)) + return false; + + tmp /= sizeof(u32); + if (tmp != cmd_cnt) { + pr_err("%s: request property number(%d) not match command count(%d)\n", + __func__, tmp, cmd_cnt); + return false; + } + + *target = kcalloc(tmp, sizeof(u32), GFP_KERNEL); + if (IS_ERR_OR_NULL(*target)) { + pr_err("%s: Error allocating memory for property\n", + __func__); + return false; + } + + if (of_property_read_u32_array(np, prop_key, *target, tmp)) { + pr_err("%s: cannot get values from dts\n", __func__); + kfree(*target); + *target = NULL; + return false; + } + + return true; + } + + static void mdss_dsi_parse_esd_params(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) + { + u32 tmp; + u32 i, status_len, *lenp; + int rc; + struct property *data; + const char *string; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + + pinfo->esd_check_enabled = of_property_read_bool(np, + "qcom,esd-check-enabled"); + + if (!pinfo->esd_check_enabled) + return; + + ctrl->status_mode = ESD_MAX; + rc = of_property_read_string(np, + "qcom,mdss-dsi-panel-status-check-mode", &string); + if (!rc) { + if (!strcmp(string, "bta_check")) { + ctrl->status_mode = ESD_BTA; + } else if (!strcmp(string, "reg_read")) { + ctrl->status_mode = ESD_REG; + ctrl->check_read_status = + mdss_dsi_gen_read_status; + } else if (!strcmp(string, "reg_read_nt35596")) { + ctrl->status_mode = ESD_REG_NT35596; + ctrl->status_error_count = 0; + ctrl->check_read_status = + mdss_dsi_nt35596_read_status; + } else if (!strcmp(string, "te_signal_check")) { + if (pinfo->mipi.mode == DSI_CMD_MODE) { + ctrl->status_mode = ESD_TE; + } else { + pr_err("TE-ESD not valid for video mode\n"); + goto error; + } + } else { + pr_err("No valid panel-status-check-mode string\n"); + goto error; + } + } + + if ((ctrl->status_mode == ESD_BTA) || (ctrl->status_mode == ESD_TE) || + (ctrl->status_mode == ESD_MAX)) + return; + + mdss_dsi_parse_dcs_cmds(np, &ctrl->status_cmds, + "qcom,mdss-dsi-panel-status-command", + "qcom,mdss-dsi-panel-status-command-state"); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-max-error-count", + &tmp); + ctrl->max_status_error_count = (!rc ? tmp : 0); + + if (!mdss_dsi_parse_esd_status_len(np, + "qcom,mdss-dsi-panel-status-read-length", + &ctrl->status_cmds_rlen, ctrl->status_cmds.cmd_cnt)) { + pinfo->esd_check_enabled = false; + return; + } + + if (mdss_dsi_parse_esd_status_len(np, + "qcom,mdss-dsi-panel-status-valid-params", + &ctrl->status_valid_params, ctrl->status_cmds.cmd_cnt)) { + if (!mdss_dsi_parse_esd_check_valid_params(ctrl)) + goto error1; + } + + status_len = 0; + lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; + for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) + status_len += lenp[i]; + + data = of_find_property(np, "qcom,mdss-dsi-panel-status-value", &tmp); + tmp /= sizeof(u32); + if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) { + ctrl->groups = tmp / status_len; + } else { + pr_err("%s: Error parse panel-status-value\n", __func__); + goto error1; + } + + ctrl->status_value = kzalloc(sizeof(u32) * status_len * ctrl->groups, + GFP_KERNEL); + if (!ctrl->status_value) + goto error1; + + ctrl->return_buf = kcalloc(status_len * ctrl->groups, + sizeof(unsigned char), GFP_KERNEL); + if (!ctrl->return_buf) + goto error2; + + rc = of_property_read_u32_array(np, + "qcom,mdss-dsi-panel-status-value", + ctrl->status_value, ctrl->groups * status_len); + if (rc) { + pr_debug("%s: Error reading panel status values\n", + __func__); + memset(ctrl->status_value, 0, ctrl->groups * status_len); + } + + return; + + error2: + kfree(ctrl->status_value); + error1: + kfree(ctrl->status_valid_params); + kfree(ctrl->status_cmds_rlen); + error: + pinfo->esd_check_enabled = false; + } + + static void mdss_dsi_parse_partial_update_caps(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) + { + struct mdss_panel_info *pinfo; + const char *data; + + pinfo = &ctrl->panel_data.panel_info; + + data = of_get_property(np, "qcom,partial-update-enabled", NULL); + if (data && !strcmp(data, "single_roi")) + pinfo->partial_update_supported = + PU_SINGLE_ROI; + else if (data && !strcmp(data, "dual_roi")) + pinfo->partial_update_supported = + PU_DUAL_ROI; + else if (data && !strcmp(data, "none")) + pinfo->partial_update_supported = + PU_NOT_SUPPORTED; + else + pinfo->partial_update_supported = + PU_NOT_SUPPORTED; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + pinfo->partial_update_enabled = pinfo->partial_update_supported; + pr_info("%s: partial_update_enabled=%d\n", __func__, + pinfo->partial_update_enabled); + ctrl->set_col_page_addr = mdss_dsi_set_col_page_addr; + if (pinfo->partial_update_enabled) { + pinfo->partial_update_roi_merge = + of_property_read_bool(np, + "qcom,partial-update-roi-merge"); + } + } + } + + static int mdss_dsi_parse_panel_features(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) + { + struct mdss_panel_info *pinfo; + + if (!np || !ctrl) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl->panel_data.panel_info; + + mdss_dsi_parse_partial_update_caps(np, ctrl); + + pinfo->dcs_cmd_by_left = of_property_read_bool(np, + "qcom,dcs-cmd-by-left"); + + pinfo->ulps_feature_enabled = of_property_read_bool(np, + "qcom,ulps-enabled"); + pr_info("%s: ulps feature %s\n", __func__, + (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); + + pinfo->ulps_suspend_enabled = of_property_read_bool(np, + "qcom,suspend-ulps-enabled"); + pr_info("%s: ulps during suspend feature %s", __func__, + (pinfo->ulps_suspend_enabled ? "enabled" : "disabled")); + + mdss_dsi_parse_dms_config(np, ctrl); + + pinfo->panel_ack_disabled = pinfo->sim_panel_mode ? + 1 : of_property_read_bool(np, "qcom,panel-ack-disabled"); + + pinfo->allow_phy_power_off = of_property_read_bool(np, + "qcom,panel-allow-phy-poweroff"); + + mdss_dsi_parse_esd_params(np, ctrl); + + if (pinfo->panel_ack_disabled && pinfo->esd_check_enabled) { + pr_warn("ESD should not be enabled if panel ACK is disabled\n"); + pinfo->esd_check_enabled = false; + } + + if (ctrl->disp_en_gpio <= 0) { + ctrl->disp_en_gpio = of_get_named_gpio( + np, + "qcom,5v-boost-gpio", 0); + + if (!gpio_is_valid(ctrl->disp_en_gpio)) + pr_debug("%s:%d, Disp_en gpio not specified\n", + __func__, __LINE__); + } + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, + "qcom,mdss-dsi-lp-mode-on", NULL); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, + "qcom,mdss-dsi-lp-mode-off", NULL); + + return 0; + } + + static void mdss_dsi_parse_panel_horizintal_line_idle(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) + { + const u32 *src; + int i, len, cnt; + struct panel_horizontal_idle *kp; + + if (!np || !ctrl) { + pr_err("%s: Invalid arguments\n", __func__); + return; + } + + src = of_get_property(np, "qcom,mdss-dsi-hor-line-idle", &len); + if (!src || len == 0) + return; + + cnt = len % 3; /* 3 fields per entry */ + if (cnt) { + pr_err("%s: invalid horizontal idle len=%d\n", __func__, len); + return; + } + + cnt = len / sizeof(u32); + + kp = kzalloc(sizeof(*kp) * (cnt / 3), GFP_KERNEL); + if (kp == NULL) { + pr_err("%s: No memory\n", __func__); + return; + } + + ctrl->line_idle = kp; + for (i = 0; i < cnt; i += 3) { + kp->min = be32_to_cpu(src[i]); + kp->max = be32_to_cpu(src[i+1]); + kp->idle = be32_to_cpu(src[i+2]); + kp++; + ctrl->horizontal_idle_cnt++; + } + + /* + * idle is enabled for this controller, this will be used to + * enable/disable burst mode since both features are mutually + * exclusive. + */ + ctrl->idle_enabled = true; + + pr_debug("%s: horizontal_idle_cnt=%d\n", __func__, + ctrl->horizontal_idle_cnt); + } + + static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node, + struct mdss_panel_info *pinfo) + { + int rc = 0; + rc = of_property_read_u32(pan_node, + "qcom,mdss-dsi-min-refresh-rate", + &pinfo->min_fps); + if (rc) { + pr_warn("%s:%d, Unable to read min refresh rate\n", + __func__, __LINE__); + + /* + * If min refresh rate is not specified, set it to the + * default panel refresh rate. + */ + pinfo->min_fps = pinfo->mipi.frame_rate; + rc = 0; + } + + rc = of_property_read_u32(pan_node, + "qcom,mdss-dsi-max-refresh-rate", + &pinfo->max_fps); + if (rc) { + pr_warn("%s:%d, Unable to read max refresh rate\n", + __func__, __LINE__); + + /* + * Since max refresh rate was not specified when dynamic + * fps is enabled, using the default panel refresh rate + * as max refresh rate supported. + */ + pinfo->max_fps = pinfo->mipi.frame_rate; + rc = 0; + } + + pr_info("dyn_fps: min = %d, max = %d\n", + pinfo->min_fps, pinfo->max_fps); + return rc; + } + + static void mdss_dsi_parse_dfps_config(struct device_node *pan_node, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + const char *data; + bool dynamic_fps; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + + dynamic_fps = of_property_read_bool(pan_node, + "qcom,mdss-dsi-pan-enable-dynamic-fps"); + + if (!dynamic_fps) + return; + + pinfo->dynamic_fps = true; + data = of_get_property(pan_node, "qcom,mdss-dsi-pan-fps-update", NULL); + if (data) { + if (!strcmp(data, "dfps_suspend_resume_mode")) { + pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; + pr_debug("dfps mode: suspend/resume\n"); + } else if (!strcmp(data, "dfps_immediate_clk_mode")) { + pinfo->dfps_update = DFPS_IMMEDIATE_CLK_UPDATE_MODE; + pr_debug("dfps mode: Immediate clk\n"); + } else if (!strcmp(data, "dfps_immediate_porch_mode_hfp")) { + pinfo->dfps_update = + DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP; + pr_debug("dfps mode: Immediate porch HFP\n"); + } else if (!strcmp(data, "dfps_immediate_porch_mode_vfp")) { + pinfo->dfps_update = + DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP; + pr_debug("dfps mode: Immediate porch VFP\n"); + } else { + pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; + pr_debug("default dfps mode: suspend/resume\n"); + } + } else { + pinfo->dynamic_fps = false; + pr_debug("dfps update mode not configured: disable\n"); + } + pinfo->new_fps = pinfo->mipi.frame_rate; + pinfo->current_fps = pinfo->mipi.frame_rate; + + return; + } + + int mdss_panel_parse_bl_settings(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + const char *data; + int rc = 0; + u32 tmp; + + ctrl_pdata->bklt_ctrl = UNKNOWN_CTRL; + data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL); + if (data) { + if (!strcmp(data, "bl_ctrl_wled")) { + led_trigger_register_simple("bkl-trigger", + &bl_led_trigger); + pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", + __func__); + ctrl_pdata->bklt_ctrl = BL_WLED; + } else if (!strcmp(data, "bl_ctrl_pwm")) { + ctrl_pdata->bklt_ctrl = BL_PWM; + ctrl_pdata->pwm_pmi = of_property_read_bool(np, + "qcom,mdss-dsi-bl-pwm-pmi"); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-bl-pmic-pwm-frequency", &tmp); + if (rc) { + pr_err("%s:%d, Error, panel pwm_period\n", + __func__, __LINE__); + return -EINVAL; + } + ctrl_pdata->pwm_period = tmp; + if (ctrl_pdata->pwm_pmi) { + ctrl_pdata->pwm_bl = of_pwm_get(np, NULL); + if (IS_ERR(ctrl_pdata->pwm_bl)) { + pr_err("%s: Error, pwm device\n", __func__); - } - } else if (!strcmp(data, "bl_ctrl_dcs")) { - ctrl_pdata->bklt_ctrl = BL_DCS_CMD; - data = of_get_property(np, - "qcom,mdss-dsi-bl-dcs-command-state", NULL); - if (data && !strcmp(data, "dsi_hs_mode")) - ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE; - else - ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE; + ctrl_pdata->pwm_bl = NULL; + return -EINVAL; + } + } else { + rc = of_property_read_u32(np, + "qcom,mdss-dsi-bl-pmic-bank-select", + &tmp); + if (rc) { + pr_err("%s:%d, Error, lpg channel\n", + __func__, __LINE__); + return -EINVAL; + } + ctrl_pdata->pwm_lpg_chan = tmp; + tmp = of_get_named_gpio(np, + "qcom,mdss-dsi-pwm-gpio", 0); + ctrl_pdata->pwm_pmic_gpio = tmp; + pr_debug("%s: Configured PWM bklt ctrl\n", + __func__); + } + } else if (!strcmp(data, "bl_ctrl_dcs")) { + ctrl_pdata->bklt_ctrl = BL_DCS_CMD; + data = of_get_property(np, + "qcom,mdss-dsi-bl-dcs-command-state", NULL); + if (data && !strcmp(data, "dsi_hs_mode")) + ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE; + else + ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE; + + pr_debug("%s: Configured DCS_CMD bklt ctrl\n", + __func__); + } + } + return 0; + } + + int mdss_dsi_panel_timing_switch(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_timing *timing) + { + struct dsi_panel_timing *pt; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + int i; + + if (!timing) + return -EINVAL; + + if (timing == ctrl->panel_data.current_timing) { + pr_warn("%s: panel timing \"%s\" already set\n", __func__, + timing->name); + return 0; /* nothing to do */ + } + + pr_debug("%s: ndx=%d switching to panel timing \"%s\"\n", __func__, + ctrl->ndx, timing->name); + + mdss_panel_info_from_timing(timing, pinfo); + + pt = container_of(timing, struct dsi_panel_timing, timing); + pinfo->mipi.t_clk_pre = pt->t_clk_pre; + pinfo->mipi.t_clk_post = pt->t_clk_post; + + for (i = 0; i < ARRAY_SIZE(pt->phy_timing); i++) + pinfo->mipi.dsi_phy_db.timing[i] = pt->phy_timing[i]; + + for (i = 0; i < ARRAY_SIZE(pt->phy_timing_8996); i++) + pinfo->mipi.dsi_phy_db.timing_8996[i] = pt->phy_timing_8996[i]; + + ctrl->on_cmds = pt->on_cmds; + ctrl->post_panel_on_cmds = pt->post_panel_on_cmds; + + ctrl->panel_data.current_timing = timing; + if (!timing->clk_rate) + ctrl->refresh_clk_rate = true; + mdss_dsi_clk_refresh(&ctrl->panel_data, ctrl->update_phy_timing); + + return 0; + } + + void mdss_dsi_unregister_bl_settings(struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + if (ctrl_pdata->bklt_ctrl == BL_WLED) + led_trigger_unregister_simple(bl_led_trigger); + } + + static int mdss_dsi_panel_timing_from_dt(struct device_node *np, + struct dsi_panel_timing *pt, + struct mdss_panel_data *panel_data) + { + u32 tmp; + u64 tmp64; + int rc, i, len; + const char *data; + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_panel_info *pinfo; + bool phy_timings_present = false; + + pinfo = &panel_data->panel_info; + + ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, + panel_data); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-width", &tmp); + if (rc) { + pr_err("%s:%d, panel width not specified\n", + __func__, __LINE__); + return -EINVAL; + } + pt->timing.xres = tmp; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-height", &tmp); + if (rc) { + pr_err("%s:%d, panel height not specified\n", + __func__, __LINE__); + return -EINVAL; + } + pt->timing.yres = tmp; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp); + pt->timing.h_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-back-porch", &tmp); + pt->timing.h_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-pulse-width", &tmp); + pt->timing.h_pulse_width = (!rc ? tmp : 2); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-sync-skew", &tmp); + pt->timing.hsync_skew = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-back-porch", &tmp); + pt->timing.v_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-front-porch", &tmp); + pt->timing.v_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-pulse-width", &tmp); + pt->timing.v_pulse_width = (!rc ? tmp : 2); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-left-border", &tmp); + pt->timing.border_left = !rc ? tmp : 0; + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-right-border", &tmp); + pt->timing.border_right = !rc ? tmp : 0; + + /* overriding left/right borders for split display cases */ + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { + if (panel_data->next) + pt->timing.border_right = 0; + else + pt->timing.border_left = 0; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-top-border", &tmp); + pt->timing.border_top = !rc ? tmp : 0; + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-bottom-border", &tmp); + pt->timing.border_bottom = !rc ? tmp : 0; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-framerate", &tmp); + pt->timing.frame_rate = !rc ? tmp : DEFAULT_FRAME_RATE; + rc = of_property_read_u64(np, "qcom,mdss-dsi-panel-clockrate", &tmp64); + if (rc == -EOVERFLOW) { + tmp64 = 0; + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64); + } + pt->timing.clk_rate = !rc ? tmp64 : 0; + + data = of_get_property(np, "qcom,mdss-dsi-panel-timings", &len); + if ((!data) || (len != 12)) { + pr_debug("%s:%d, Unable to read Phy timing settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pt->phy_timing[i] = data[i]; + phy_timings_present = true; + } + + data = of_get_property(np, "qcom,mdss-dsi-panel-timings-phy-v2", &len); + if ((!data) || (len != 40)) { + pr_debug("%s:%d, Unable to read phy-v2 lane timing settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pt->phy_timing_8996[i] = data[i]; + phy_timings_present = true; + } + if (!phy_timings_present) { + pr_err("%s: phy timing settings not present\n", __func__); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-pre", &tmp); + pt->t_clk_pre = (!rc ? tmp : 0x24); + rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); + pt->t_clk_post = (!rc ? tmp : 0x03); + + if (np->name) { + pt->timing.name = kstrdup(np->name, GFP_KERNEL); + pr_info("%s: found new timing \"%s\" (%pK)\n", __func__, + np->name, &pt->timing); + } + + return 0; + } + + static int mdss_dsi_panel_config_res_properties(struct device_node *np, + struct dsi_panel_timing *pt, + struct mdss_panel_data *panel_data, + bool default_timing) + { + int rc = 0; + + mdss_dsi_parse_roi_alignment(np, pt); + + mdss_dsi_parse_dcs_cmds(np, &pt->on_cmds, + "qcom,mdss-dsi-on-command", + "qcom,mdss-dsi-on-command-state"); + + mdss_dsi_parse_dcs_cmds(np, &pt->post_panel_on_cmds, + "qcom,mdss-dsi-post-panel-on-command", NULL); + + mdss_dsi_parse_dcs_cmds(np, &pt->switch_cmds, + "qcom,mdss-dsi-timing-switch-command", + "qcom,mdss-dsi-timing-switch-command-state"); + + rc = mdss_dsi_parse_topology_config(np, pt, panel_data, default_timing); + if (rc) { + pr_err("%s: parsing compression params failed. rc:%d\n", + __func__, rc); + return rc; + } + + mdss_panel_parse_te_params(np, &pt->timing); + return rc; + } + + static int mdss_panel_parse_display_timings(struct device_node *np, + struct mdss_panel_data *panel_data) + { + struct mdss_dsi_ctrl_pdata *ctrl; + struct dsi_panel_timing *modedb; + struct device_node *timings_np; + struct device_node *entry; + int num_timings, rc; + int i = 0, active_ndx = 0; + bool default_timing = false; + + ctrl = container_of(panel_data, struct mdss_dsi_ctrl_pdata, panel_data); + + INIT_LIST_HEAD(&panel_data->timings_list); + + timings_np = of_get_child_by_name(np, "qcom,mdss-dsi-display-timings"); + if (!timings_np) { + struct dsi_panel_timing pt; + memset(&pt, 0, sizeof(struct dsi_panel_timing)); + + /* + * display timings node is not available, fallback to reading + * timings directly from root node instead + */ + pr_debug("reading display-timings from panel node\n"); + rc = mdss_dsi_panel_timing_from_dt(np, &pt, panel_data); + if (!rc) { + mdss_dsi_panel_config_res_properties(np, &pt, + panel_data, true); + rc = mdss_dsi_panel_timing_switch(ctrl, &pt.timing); + } + return rc; + } + + num_timings = of_get_child_count(timings_np); + if (num_timings == 0) { + pr_err("no timings found within display-timings\n"); + rc = -EINVAL; + goto exit; + } + + modedb = kcalloc(num_timings, sizeof(*modedb), GFP_KERNEL); + if (!modedb) { + rc = -ENOMEM; + goto exit; + } + + for_each_child_of_node(timings_np, entry) { + rc = mdss_dsi_panel_timing_from_dt(entry, (modedb + i), + panel_data); + if (rc) { + kfree(modedb); + goto exit; + } + + default_timing = of_property_read_bool(entry, + "qcom,mdss-dsi-timing-default"); + if (default_timing) + active_ndx = i; + + mdss_dsi_panel_config_res_properties(entry, (modedb + i), + panel_data, default_timing); + + list_add(&modedb[i].timing.list, + &panel_data->timings_list); + i++; + } + + /* Configure default timing settings */ + rc = mdss_dsi_panel_timing_switch(ctrl, &modedb[active_ndx].timing); + if (rc) + pr_err("unable to configure default timing settings\n"); + + exit: + of_node_put(timings_np); + + return rc; + } + + static int mdss_panel_parse_dt(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) + { + u32 tmp; + int rc, len = 0; + const char *data; + static const char *pdest; + const char *bridge_chip_name; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) + pinfo->is_split_display = true; + + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-width-dimension", &tmp); + pinfo->physical_width = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-height-dimension", &tmp); + pinfo->physical_height = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-bpp", &tmp); + if (rc) { + pr_err("%s:%d, bpp not specified\n", __func__, __LINE__); + return -EINVAL; + } + pinfo->bpp = (!rc ? tmp : 24); + pinfo->mipi.mode = DSI_VIDEO_MODE; + data = of_get_property(np, "qcom,mdss-dsi-panel-type", NULL); + if (data && !strncmp(data, "dsi_cmd_mode", 12)) + pinfo->mipi.mode = DSI_CMD_MODE; + pinfo->mipi.boot_mode = pinfo->mipi.mode; + tmp = 0; + data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL); + if (data && !strcmp(data, "loose")) + pinfo->mipi.pixel_packing = 1; + else + pinfo->mipi.pixel_packing = 0; + rc = mdss_panel_get_dst_fmt(pinfo->bpp, + pinfo->mipi.mode, pinfo->mipi.pixel_packing, + &(pinfo->mipi.dst_format)); + if (rc) { + pr_debug("%s: problem determining dst format. Set Default\n", + __func__); + pinfo->mipi.dst_format = + DSI_VIDEO_DST_FORMAT_RGB888; + } + pdest = of_get_property(np, + "qcom,mdss-dsi-panel-destination", NULL); + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-underflow-color", &tmp); + pinfo->lcdc.underflow_clr = (!rc ? tmp : 0xff); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-border-color", &tmp); + pinfo->lcdc.border_clr = (!rc ? tmp : 0); + data = of_get_property(np, "qcom,mdss-dsi-panel-orientation", NULL); + if (data) { + pr_debug("panel orientation is %s\n", data); + if (!strcmp(data, "180")) + pinfo->panel_orientation = MDP_ROT_180; + else if (!strcmp(data, "hflip")) + pinfo->panel_orientation = MDP_FLIP_LR; + else if (!strcmp(data, "vflip")) + pinfo->panel_orientation = MDP_FLIP_UD; + } + + rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); + pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); + rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp); + pinfo->bl_min = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp); + pinfo->bl_max = (!rc ? tmp : 255); + ctrl_pdata->bklt_max = pinfo->bl_max; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-interleave-mode", &tmp); + pinfo->mipi.interleave_mode = (!rc ? tmp : 0); + + pinfo->mipi.vsync_enable = of_property_read_bool(np, + "qcom,mdss-dsi-te-check-enable"); + + if (pinfo->sim_panel_mode == SIM_SW_TE_MODE) + pinfo->mipi.hw_vsync_mode = false; + else + pinfo->mipi.hw_vsync_mode = of_property_read_bool(np, + "qcom,mdss-dsi-te-using-te-pin"); + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-h-sync-pulse", &tmp); + pinfo->mipi.pulse_mode_hsa_he = (!rc ? tmp : false); + + pinfo->mipi.hfp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hfp-power-mode"); + pinfo->mipi.hsa_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hsa-power-mode"); + pinfo->mipi.hbp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hbp-power-mode"); + pinfo->mipi.last_line_interleave_en = of_property_read_bool(np, + "qcom,mdss-dsi-last-line-interleave"); + pinfo->mipi.bllp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-bllp-power-mode"); + pinfo->mipi.eof_bllp_power_stop = of_property_read_bool( + np, "qcom,mdss-dsi-bllp-eof-power-mode"); + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + data = of_get_property(np, "qcom,mdss-dsi-traffic-mode", NULL); + if (data) { + if (!strcmp(data, "non_burst_sync_event")) + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + else if (!strcmp(data, "burst_mode")) + pinfo->mipi.traffic_mode = DSI_BURST_MODE; + } + rc = of_property_read_u32(np, + "qcom,mdss-dsi-te-dcs-command", &tmp); + pinfo->mipi.insert_dcs_cmd = + (!rc ? tmp : 1); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-wr-mem-continue", &tmp); + pinfo->mipi.wr_mem_continue = + (!rc ? tmp : 0x3c); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-wr-mem-start", &tmp); + pinfo->mipi.wr_mem_start = + (!rc ? tmp : 0x2c); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-te-pin-select", &tmp); + pinfo->mipi.te_sel = + (!rc ? tmp : 1); + rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp); + pinfo->mipi.vc = (!rc ? tmp : 0); + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; + data = of_get_property(np, "qcom,mdss-dsi-color-order", NULL); + if (data) { + if (!strcmp(data, "rgb_swap_rbg")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RBG; + else if (!strcmp(data, "rgb_swap_bgr")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BGR; + else if (!strcmp(data, "rgb_swap_brg")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BRG; + else if (!strcmp(data, "rgb_swap_grb")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GRB; + else if (!strcmp(data, "rgb_swap_gbr")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GBR; + } + pinfo->mipi.data_lane0 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-0-state"); + pinfo->mipi.data_lane1 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-1-state"); + pinfo->mipi.data_lane2 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-2-state"); + pinfo->mipi.data_lane3 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-3-state"); + + /* parse split link properties */ + rc = mdss_dsi_parse_split_link_settings(np, pinfo); + if (rc) + return rc; + + rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); + if (rc) + return rc; + + rc = mdss_dsi_parse_hdr_settings(np, pinfo); + if (rc) + return rc; + + pinfo->mipi.rx_eot_ignore = of_property_read_bool(np, + "qcom,mdss-dsi-rx-eot-ignore"); + pinfo->mipi.tx_eot_append = of_property_read_bool(np, + "qcom,mdss-dsi-tx-eot-append"); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp); + pinfo->mipi.stream = (!rc ? tmp : 0); + + data = of_get_property(np, "qcom,mdss-dsi-mode-sel-gpio-state", NULL); + if (data) { + if (!strcmp(data, "single_port")) + pinfo->mode_sel_state = MODE_SEL_SINGLE_PORT; + else if (!strcmp(data, "dual_port")) + pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; + else if (!strcmp(data, "high")) + pinfo->mode_sel_state = MODE_GPIO_HIGH; + else if (!strcmp(data, "low")) + pinfo->mode_sel_state = MODE_GPIO_LOW; + } else { + /* Set default mode as SPLIT mode */ + pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; + } + + rc = of_property_read_u32(np, "qcom,mdss-mdp-transfer-time-us", &tmp); + pinfo->mdp_transfer_time_us = (!rc ? tmp : DEFAULT_MDP_TRANSFER_TIME); + + mdss_dsi_parse_mdp_kickoff_threshold(np, pinfo); + + pinfo->mipi.lp11_init = of_property_read_bool(np, + "qcom,mdss-dsi-lp11-init"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-init-delay-us", &tmp); + pinfo->mipi.init_delay = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-post-init-delay", &tmp); + pinfo->mipi.post_init_delay = (!rc ? tmp : 0); + + mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), + "qcom,mdss-dsi-mdp-trigger"); + + mdss_dsi_parse_trigger(np, &(pinfo->mipi.dma_trigger), + "qcom,mdss-dsi-dma-trigger"); + + mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len), + "qcom,mdss-dsi-reset-sequence"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, + "qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); + + rc = of_property_read_u32(np, "qcom,adjust-timer-wakeup-ms", &tmp); + pinfo->adjust_timer_delay_ms = (!rc ? tmp : 0); + + pinfo->mipi.force_clk_lane_hs = of_property_read_bool(np, + "qcom,mdss-dsi-force-clock-lane-hs"); + + rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); + if (rc) { + pr_err("%s: failed to parse panel features\n", __func__); + goto error; + } + + mdss_dsi_parse_panel_horizintal_line_idle(np, ctrl_pdata); + + mdss_dsi_parse_dfps_config(np, ctrl_pdata); + + mdss_dsi_set_refresh_rate_range(np, pinfo); + + pinfo->is_dba_panel = of_property_read_bool(np, + "qcom,dba-panel"); +#if defined(CONFIG_IRIS2P_FULL_SUPPORT) + iris_init_params_parse(np, ctrl_pdata); + iris_init_cmd_setup(ctrl_pdata); +#endif + + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->srgb_on_cmds, + "qcom,mdss-dsi-panel-srgb-on-command", + "qcom,mdss-dsi-srgb-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->srgb_off_cmds, + "qcom,mdss-dsi-panel-srgb-off-command", + "qcom,mdss-dsi-srgb-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->night_mode_on_cmds, + "qcom,mdss-dsi-panel-night-mode-on-command", + "qcom,mdss-dsi-night-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->night_mode_off_cmds, + "qcom,mdss-dsi-panel-night-mode-off-command", + "qcom,mdss-dsi-night-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->oneplus_mode_on_cmds, + "qcom,mdss-dsi-panel-oneplus-mode-on-command", + "qcom,mdss-dsi-oneplus-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->oneplus_mode_off_cmds, + "qcom,mdss-dsi-panel-oneplus-mode-off-command", + "qcom,mdss-dsi-oneplus-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->adaption_mode_on_cmds, + "qcom,mdss-dsi-panel-adaption-mode-on-command", + "qcom,mdss-dsi-adaption-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->adaption_mode_off_cmds, + "qcom,mdss-dsi-panel-adaption-mode-off-command", + "qcom,mdss-dsi-adaption-mode-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->dci_p3_on_cmds, + "qcom,mdss-dsi-panel-dci-p3-on-command", + "qcom,mdss-dsi-dci-p3-command-state"); + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->dci_p3_off_cmds, + "qcom,mdss-dsi-panel-dci-p3-off-command", + "qcom,mdss-dsi-dci-p3-command-state"); + + + ctrl_pdata->high_brightness_panel= of_property_read_bool(np, + "qcom,mdss-dsi-high-brightness-panel"); + pr_err("high brightness panel: %d\n", ctrl_pdata->high_brightness_panel); + ctrl_pdata->bl_high2bit = of_property_read_bool(np, + "qcom,mdss-bl-high2bit"); + if (pinfo->is_dba_panel) { + bridge_chip_name = of_get_property(np, + "qcom,bridge-name", &len); + if (!bridge_chip_name || len <= 0) { + pr_err("%s:%d Unable to read qcom,bridge_name, data=%pK,len=%d\n", + __func__, __LINE__, bridge_chip_name, len); + rc = -EINVAL; + goto error; + } + strlcpy(ctrl_pdata->bridge_name, bridge_chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-host-esc-clk-freq-hz", + &pinfo->esc_clk_rate_hz); + if (rc) + pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; + pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); + + return 0; + + error: + return -EINVAL; + } + + int mdss_dsi_panel_init(struct device_node *node, + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int ndx) + { + int rc = 0; + static const char *panel_name; + struct mdss_panel_info *pinfo; + static const char *panel_manufacture; + static const char *panel_version; + static const char *backlight_manufacture; + static const char *backlight_version; + + if (!node || !ctrl_pdata) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + + pr_debug("%s:%d\n", __func__, __LINE__); + pinfo->panel_name[0] = '\0'; + panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); + if (!panel_name) { + pr_info("%s:%d, Panel name not specified\n", + __func__, __LINE__); + } else { + pr_info("%s: Panel Name = %s\n", __func__, panel_name); + strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); + } + panel_manufacture = of_get_property(node, + "qcom,mdss-dsi-panel-manufacture", NULL); + if (!panel_manufacture) + pr_info("%s:%d, panel manufacture not specified\n", __func__, __LINE__); + panel_version = of_get_property(node, "qcom,mdss-dsi-panel-version", NULL); + if (!panel_version) + pr_info("%s:%d, panel version not specified\n", __func__, __LINE__); + push_component_info(LCD, (char *)panel_version, + (char *)panel_manufacture); + backlight_manufacture = of_get_property(node, + "qcom,mdss-dsi-backlight-manufacture", NULL); + if (!backlight_manufacture) + pr_info("%s:%d, backlight manufacture not specified\n", __func__, __LINE__); + backlight_version = of_get_property(node, + "qcom,mdss-dsi-backlight-version", NULL); + if (!backlight_version) + pr_info("%s:%d, backlight version not specified\n", __func__, __LINE__); + push_component_info(BACKLIGHT, (char *)backlight_version, + (char *)backlight_manufacture); + mutex_init(&ctrl_pdata->panel_mode_lock); + + rc = mdss_panel_parse_dt(node, ctrl_pdata); + if (rc) { + pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__); + return rc; + } + + pinfo->dynamic_switch_pending = false; + pinfo->is_lpm_mode = false; + pinfo->esd_rdy = false; + pinfo->persist_mode = false; + + ctrl_pdata->on = mdss_dsi_panel_on; + ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; + ctrl_pdata->off = mdss_dsi_panel_off; + ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; + ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; + ctrl_pdata->panel_data.apply_display_setting = + mdss_dsi_panel_apply_display_setting; + ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; + + return 0; + } - pr_debug("%s: Configured DCS_CMD bklt ctrl\n", - __func__); - } - } - return 0; -} - -int mdss_dsi_panel_timing_switch(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_panel_timing *timing) -{ - struct dsi_panel_timing *pt; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - int i; - - if (!timing) - return -EINVAL; - - if (timing == ctrl->panel_data.current_timing) { - pr_warn("%s: panel timing \"%s\" already set\n", __func__, - timing->name); - return 0; /* nothing to do */ - } - - pr_debug("%s: ndx=%d switching to panel timing \"%s\"\n", __func__, - ctrl->ndx, timing->name); - - mdss_panel_info_from_timing(timing, pinfo); - - pt = container_of(timing, struct dsi_panel_timing, timing); - pinfo->mipi.t_clk_pre = pt->t_clk_pre; - pinfo->mipi.t_clk_post = pt->t_clk_post; - - for (i = 0; i < ARRAY_SIZE(pt->phy_timing); i++) - pinfo->mipi.dsi_phy_db.timing[i] = pt->phy_timing[i]; - - for (i = 0; i < ARRAY_SIZE(pt->phy_timing_8996); i++) - pinfo->mipi.dsi_phy_db.timing_8996[i] = pt->phy_timing_8996[i]; - - ctrl->on_cmds = pt->on_cmds; - ctrl->post_panel_on_cmds = pt->post_panel_on_cmds; - - ctrl->panel_data.current_timing = timing; - if (!timing->clk_rate) - ctrl->refresh_clk_rate = true; - mdss_dsi_clk_refresh(&ctrl->panel_data, ctrl->update_phy_timing); - - return 0; -} - -void mdss_dsi_unregister_bl_settings(struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - if (ctrl_pdata->bklt_ctrl == BL_WLED) - led_trigger_unregister_simple(bl_led_trigger); -} - -static int mdss_dsi_panel_timing_from_dt(struct device_node *np, - struct dsi_panel_timing *pt, - struct mdss_panel_data *panel_data) -{ - u32 tmp; - u64 tmp64; - int rc, i, len; - const char *data; - struct mdss_dsi_ctrl_pdata *ctrl_pdata; - struct mdss_panel_info *pinfo; - bool phy_timings_present = false; - - pinfo = &panel_data->panel_info; - - ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, - panel_data); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-width", &tmp); - if (rc) { - pr_err("%s:%d, panel width not specified\n", - __func__, __LINE__); - return -EINVAL; - } - pt->timing.xres = tmp; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-height", &tmp); - if (rc) { - pr_err("%s:%d, panel height not specified\n", - __func__, __LINE__); - return -EINVAL; - } - pt->timing.yres = tmp; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp); - pt->timing.h_front_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-back-porch", &tmp); - pt->timing.h_back_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-pulse-width", &tmp); - pt->timing.h_pulse_width = (!rc ? tmp : 2); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-sync-skew", &tmp); - pt->timing.hsync_skew = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-back-porch", &tmp); - pt->timing.v_back_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-front-porch", &tmp); - pt->timing.v_front_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-pulse-width", &tmp); - pt->timing.v_pulse_width = (!rc ? tmp : 2); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-left-border", &tmp); - pt->timing.border_left = !rc ? tmp : 0; - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-right-border", &tmp); - pt->timing.border_right = !rc ? tmp : 0; - - /* overriding left/right borders for split display cases */ - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { - if (panel_data->next) - pt->timing.border_right = 0; - else - pt->timing.border_left = 0; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-top-border", &tmp); - pt->timing.border_top = !rc ? tmp : 0; - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-bottom-border", &tmp); - pt->timing.border_bottom = !rc ? tmp : 0; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-framerate", &tmp); - pt->timing.frame_rate = !rc ? tmp : DEFAULT_FRAME_RATE; - rc = of_property_read_u64(np, "qcom,mdss-dsi-panel-clockrate", &tmp64); - if (rc == -EOVERFLOW) { - tmp64 = 0; - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64); - } - pt->timing.clk_rate = !rc ? tmp64 : 0; - - data = of_get_property(np, "qcom,mdss-dsi-panel-timings", &len); - if ((!data) || (len != 12)) { - pr_debug("%s:%d, Unable to read Phy timing settings", - __func__, __LINE__); - } else { - for (i = 0; i < len; i++) - pt->phy_timing[i] = data[i]; - phy_timings_present = true; - } - - data = of_get_property(np, "qcom,mdss-dsi-panel-timings-phy-v2", &len); - if ((!data) || (len != 40)) { - pr_debug("%s:%d, Unable to read phy-v2 lane timing settings", - __func__, __LINE__); - } else { - for (i = 0; i < len; i++) - pt->phy_timing_8996[i] = data[i]; - phy_timings_present = true; - } - if (!phy_timings_present) { - pr_err("%s: phy timing settings not present\n", __func__); - return -EINVAL; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-pre", &tmp); - pt->t_clk_pre = (!rc ? tmp : 0x24); - rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); - pt->t_clk_post = (!rc ? tmp : 0x03); - - if (np->name) { - pt->timing.name = kstrdup(np->name, GFP_KERNEL); - pr_info("%s: found new timing \"%s\" (%pK)\n", __func__, - np->name, &pt->timing); - } - - return 0; -} - -static int mdss_dsi_panel_config_res_properties(struct device_node *np, - struct dsi_panel_timing *pt, - struct mdss_panel_data *panel_data, - bool default_timing) -{ - int rc = 0; - - mdss_dsi_parse_roi_alignment(np, pt); - - mdss_dsi_parse_dcs_cmds(np, &pt->on_cmds, - "qcom,mdss-dsi-on-command", - "qcom,mdss-dsi-on-command-state"); - - mdss_dsi_parse_dcs_cmds(np, &pt->post_panel_on_cmds, - "qcom,mdss-dsi-post-panel-on-command", NULL); - - mdss_dsi_parse_dcs_cmds(np, &pt->switch_cmds, - "qcom,mdss-dsi-timing-switch-command", - "qcom,mdss-dsi-timing-switch-command-state"); - - rc = mdss_dsi_parse_topology_config(np, pt, panel_data, default_timing); - if (rc) { - pr_err("%s: parsing compression params failed. rc:%d\n", - __func__, rc); - return rc; - } - - mdss_panel_parse_te_params(np, &pt->timing); - return rc; -} - -static int mdss_panel_parse_display_timings(struct device_node *np, - struct mdss_panel_data *panel_data) -{ - struct mdss_dsi_ctrl_pdata *ctrl; - struct dsi_panel_timing *modedb; - struct device_node *timings_np; - struct device_node *entry; - int num_timings, rc; - int i = 0, active_ndx = 0; - bool default_timing = false; - - ctrl = container_of(panel_data, struct mdss_dsi_ctrl_pdata, panel_data); - - INIT_LIST_HEAD(&panel_data->timings_list); - - timings_np = of_get_child_by_name(np, "qcom,mdss-dsi-display-timings"); - if (!timings_np) { - struct dsi_panel_timing pt; - memset(&pt, 0, sizeof(struct dsi_panel_timing)); - - /* - * display timings node is not available, fallback to reading - * timings directly from root node instead - */ - pr_debug("reading display-timings from panel node\n"); - rc = mdss_dsi_panel_timing_from_dt(np, &pt, panel_data); - if (!rc) { - mdss_dsi_panel_config_res_properties(np, &pt, - panel_data, true); - rc = mdss_dsi_panel_timing_switch(ctrl, &pt.timing); - } - return rc; - } - - num_timings = of_get_child_count(timings_np); - if (num_timings == 0) { - pr_err("no timings found within display-timings\n"); - rc = -EINVAL; - goto exit; - } - - modedb = kcalloc(num_timings, sizeof(*modedb), GFP_KERNEL); - if (!modedb) { - rc = -ENOMEM; - goto exit; - } - - for_each_child_of_node(timings_np, entry) { - rc = mdss_dsi_panel_timing_from_dt(entry, (modedb + i), - panel_data); - if (rc) { - kfree(modedb); - goto exit; - } - - default_timing = of_property_read_bool(entry, - "qcom,mdss-dsi-timing-default"); - if (default_timing) - active_ndx = i; - - mdss_dsi_panel_config_res_properties(entry, (modedb + i), - panel_data, default_timing); - - list_add(&modedb[i].timing.list, - &panel_data->timings_list); - i++; - } - - /* Configure default timing settings */ - rc = mdss_dsi_panel_timing_switch(ctrl, &modedb[active_ndx].timing); - if (rc) - pr_err("unable to configure default timing settings\n"); - -exit: - of_node_put(timings_np); - - return rc; -} - -static int mdss_panel_parse_dt(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) -{ - u32 tmp; - int rc, len = 0; - const char *data; - static const char *pdest; - const char *bridge_chip_name; - struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); - - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) - pinfo->is_split_display = true; - - rc = of_property_read_u32(np, - "qcom,mdss-pan-physical-width-dimension", &tmp); - pinfo->physical_width = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-pan-physical-height-dimension", &tmp); - pinfo->physical_height = (!rc ? tmp : 0); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-bpp", &tmp); - if (rc) { - pr_err("%s:%d, bpp not specified\n", __func__, __LINE__); - return -EINVAL; - } - pinfo->bpp = (!rc ? tmp : 24); - pinfo->mipi.mode = DSI_VIDEO_MODE; - data = of_get_property(np, "qcom,mdss-dsi-panel-type", NULL); - if (data && !strncmp(data, "dsi_cmd_mode", 12)) - pinfo->mipi.mode = DSI_CMD_MODE; - pinfo->mipi.boot_mode = pinfo->mipi.mode; - tmp = 0; - data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL); - if (data && !strcmp(data, "loose")) - pinfo->mipi.pixel_packing = 1; - else - pinfo->mipi.pixel_packing = 0; - rc = mdss_panel_get_dst_fmt(pinfo->bpp, - pinfo->mipi.mode, pinfo->mipi.pixel_packing, - &(pinfo->mipi.dst_format)); - if (rc) { - pr_debug("%s: problem determining dst format. Set Default\n", - __func__); - pinfo->mipi.dst_format = - DSI_VIDEO_DST_FORMAT_RGB888; - } - pdest = of_get_property(np, - "qcom,mdss-dsi-panel-destination", NULL); - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-underflow-color", &tmp); - pinfo->lcdc.underflow_clr = (!rc ? tmp : 0xff); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-border-color", &tmp); - pinfo->lcdc.border_clr = (!rc ? tmp : 0); - data = of_get_property(np, "qcom,mdss-dsi-panel-orientation", NULL); - if (data) { - pr_debug("panel orientation is %s\n", data); - if (!strcmp(data, "180")) - pinfo->panel_orientation = MDP_ROT_180; - else if (!strcmp(data, "hflip")) - pinfo->panel_orientation = MDP_FLIP_LR; - else if (!strcmp(data, "vflip")) - pinfo->panel_orientation = MDP_FLIP_UD; - } - - rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); - pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); - rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp); - pinfo->bl_min = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp); - pinfo->bl_max = (!rc ? tmp : 255); - ctrl_pdata->bklt_max = pinfo->bl_max; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-interleave-mode", &tmp); - pinfo->mipi.interleave_mode = (!rc ? tmp : 0); - - pinfo->mipi.vsync_enable = of_property_read_bool(np, - "qcom,mdss-dsi-te-check-enable"); - - if (pinfo->sim_panel_mode == SIM_SW_TE_MODE) - pinfo->mipi.hw_vsync_mode = false; - else - pinfo->mipi.hw_vsync_mode = of_property_read_bool(np, - "qcom,mdss-dsi-te-using-te-pin"); - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-h-sync-pulse", &tmp); - pinfo->mipi.pulse_mode_hsa_he = (!rc ? tmp : false); - - pinfo->mipi.hfp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hfp-power-mode"); - pinfo->mipi.hsa_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hsa-power-mode"); - pinfo->mipi.hbp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hbp-power-mode"); - pinfo->mipi.last_line_interleave_en = of_property_read_bool(np, - "qcom,mdss-dsi-last-line-interleave"); - pinfo->mipi.bllp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-bllp-power-mode"); - pinfo->mipi.eof_bllp_power_stop = of_property_read_bool( - np, "qcom,mdss-dsi-bllp-eof-power-mode"); - pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; - data = of_get_property(np, "qcom,mdss-dsi-traffic-mode", NULL); - if (data) { - if (!strcmp(data, "non_burst_sync_event")) - pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; - else if (!strcmp(data, "burst_mode")) - pinfo->mipi.traffic_mode = DSI_BURST_MODE; - } - rc = of_property_read_u32(np, - "qcom,mdss-dsi-te-dcs-command", &tmp); - pinfo->mipi.insert_dcs_cmd = - (!rc ? tmp : 1); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-wr-mem-continue", &tmp); - pinfo->mipi.wr_mem_continue = - (!rc ? tmp : 0x3c); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-wr-mem-start", &tmp); - pinfo->mipi.wr_mem_start = - (!rc ? tmp : 0x2c); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-te-pin-select", &tmp); - pinfo->mipi.te_sel = - (!rc ? tmp : 1); - rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp); - pinfo->mipi.vc = (!rc ? tmp : 0); - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; - data = of_get_property(np, "qcom,mdss-dsi-color-order", NULL); - if (data) { - if (!strcmp(data, "rgb_swap_rbg")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RBG; - else if (!strcmp(data, "rgb_swap_bgr")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BGR; - else if (!strcmp(data, "rgb_swap_brg")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BRG; - else if (!strcmp(data, "rgb_swap_grb")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GRB; - else if (!strcmp(data, "rgb_swap_gbr")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GBR; - } - pinfo->mipi.data_lane0 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-0-state"); - pinfo->mipi.data_lane1 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-1-state"); - pinfo->mipi.data_lane2 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-2-state"); - pinfo->mipi.data_lane3 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-3-state"); - - /* parse split link properties */ - rc = mdss_dsi_parse_split_link_settings(np, pinfo); - if (rc) - return rc; - - rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); - if (rc) - return rc; - - rc = mdss_dsi_parse_hdr_settings(np, pinfo); - if (rc) - return rc; - - pinfo->mipi.rx_eot_ignore = of_property_read_bool(np, - "qcom,mdss-dsi-rx-eot-ignore"); - pinfo->mipi.tx_eot_append = of_property_read_bool(np, - "qcom,mdss-dsi-tx-eot-append"); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp); - pinfo->mipi.stream = (!rc ? tmp : 0); - - data = of_get_property(np, "qcom,mdss-dsi-mode-sel-gpio-state", NULL); - if (data) { - if (!strcmp(data, "single_port")) - pinfo->mode_sel_state = MODE_SEL_SINGLE_PORT; - else if (!strcmp(data, "dual_port")) - pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; - else if (!strcmp(data, "high")) - pinfo->mode_sel_state = MODE_GPIO_HIGH; - else if (!strcmp(data, "low")) - pinfo->mode_sel_state = MODE_GPIO_LOW; - } else { - /* Set default mode as SPLIT mode */ - pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; - } - - rc = of_property_read_u32(np, "qcom,mdss-mdp-transfer-time-us", &tmp); - pinfo->mdp_transfer_time_us = (!rc ? tmp : DEFAULT_MDP_TRANSFER_TIME); - - mdss_dsi_parse_mdp_kickoff_threshold(np, pinfo); - - pinfo->mipi.lp11_init = of_property_read_bool(np, - "qcom,mdss-dsi-lp11-init"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-init-delay-us", &tmp); - pinfo->mipi.init_delay = (!rc ? tmp : 0); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-post-init-delay", &tmp); - pinfo->mipi.post_init_delay = (!rc ? tmp : 0); - - mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), - "qcom,mdss-dsi-mdp-trigger"); - - mdss_dsi_parse_trigger(np, &(pinfo->mipi.dma_trigger), - "qcom,mdss-dsi-dma-trigger"); - - mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len), - "qcom,mdss-dsi-reset-sequence"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, - "qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); - - rc = of_property_read_u32(np, "qcom,adjust-timer-wakeup-ms", &tmp); - pinfo->adjust_timer_delay_ms = (!rc ? tmp : 0); - - pinfo->mipi.force_clk_lane_hs = of_property_read_bool(np, - "qcom,mdss-dsi-force-clock-lane-hs"); - - rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); - if (rc) { - pr_err("%s: failed to parse panel features\n", __func__); - goto error; - } - - mdss_dsi_parse_panel_horizintal_line_idle(np, ctrl_pdata); - - mdss_dsi_parse_dfps_config(np, ctrl_pdata); - - mdss_dsi_set_refresh_rate_range(np, pinfo); - - pinfo->is_dba_panel = of_property_read_bool(np, - "qcom,dba-panel"); - - if (pinfo->is_dba_panel) { - bridge_chip_name = of_get_property(np, - "qcom,bridge-name", &len); - if (!bridge_chip_name || len <= 0) { - pr_err("%s:%d Unable to read qcom,bridge_name, data=%pK,len=%d\n", - __func__, __LINE__, bridge_chip_name, len); - rc = -EINVAL; - goto error; - } - strlcpy(ctrl_pdata->bridge_name, bridge_chip_name, - MSM_DBA_CHIP_NAME_MAX_LEN); - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-host-esc-clk-freq-hz", - &pinfo->esc_clk_rate_hz); - if (rc) - pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; - pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); - - return 0; - -error: - return -EINVAL; -} - -int mdss_dsi_panel_init(struct device_node *node, - struct mdss_dsi_ctrl_pdata *ctrl_pdata, - int ndx) -{ - int rc = 0; - static const char *panel_name; - struct mdss_panel_info *pinfo; - - if (!node || !ctrl_pdata) { - pr_err("%s: Invalid arguments\n", __func__); - return -ENODEV; - } - - pinfo = &ctrl_pdata->panel_data.panel_info; - - pr_debug("%s:%d\n", __func__, __LINE__); - pinfo->panel_name[0] = '\0'; - panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); - if (!panel_name) { - pr_info("%s:%d, Panel name not specified\n", - __func__, __LINE__); - } else { - pr_info("%s: Panel Name = %s\n", __func__, panel_name); - strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); - } - rc = mdss_panel_parse_dt(node, ctrl_pdata); - if (rc) { - pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__); - return rc; - } - - pinfo->dynamic_switch_pending = false; - pinfo->is_lpm_mode = false; - pinfo->esd_rdy = false; - pinfo->persist_mode = false; - - ctrl_pdata->on = mdss_dsi_panel_on; - ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; - ctrl_pdata->off = mdss_dsi_panel_off; - ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; - ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; - ctrl_pdata->panel_data.apply_display_setting = - mdss_dsi_panel_apply_display_setting; - ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; - - return 0; -} diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 0f24f66dbcc6..8baf07e792b4 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -117,6 +117,10 @@ irqreturn_t hw_vsync_handler(int irq, void *data) return IRQ_HANDLED; } + complete(&ctrl_pdata->te_comp); + + /* #endif */ + if (pstatus_data) mod_delayed_work(system_wq, &pstatus_data->check_status, msecs_to_jiffies(interval)); diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 6c4db0f1f5bd..672547acbd91 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -886,6 +886,257 @@ end: return len; } + +static ssize_t mdss_fb_get_srgb_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_GET_SRGB_MODE, + NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->sRGB Mode OFF\n" + "1-->sRGB Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_srgb_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_SRGB_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set sRGB mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(SRGB, S_IRUGO | S_IWUSR, + mdss_fb_get_srgb_mode, mdss_fb_set_srgb_mode); + + +static ssize_t mdss_fb_get_adobe_rgb_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, + MDSS_EVENT_PANEL_GET_ADOBE_RGB_MODE, NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->Adobe RGB Mode OFF\n" + "1-->Adobe RGB Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_adobe_rgb_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_ADOBE_RGB_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set Adobe RGB mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(Adobe_RGB, S_IRUGO | S_IWUSR, + mdss_fb_get_adobe_rgb_mode, mdss_fb_set_adobe_rgb_mode); + +static ssize_t mdss_fb_get_dci_p3_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_GET_DCI_P3_MODE, + NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->DCI-P3 Mode OFF\n" + "1-->DCI-P3 Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_dci_p3_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_DCI_P3_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set DCI P3 mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(DCI_P3, S_IRUGO | S_IWUSR, + mdss_fb_get_dci_p3_mode, mdss_fb_set_dci_p3_mode); + +static ssize_t mdss_fb_get_night_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_GET_NIGHT_MODE, + NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->Night Mode OFF\n" + "1-->Night Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_night_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_NIGHT_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set Night mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(night_mode, S_IRUGO | S_IWUSR, + mdss_fb_get_night_mode, mdss_fb_set_night_mode); + +static ssize_t mdss_fb_get_oneplus_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_GET_ONEPLUS_MODE, + NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->Oneplus Mode OFF\n" + "1-->Oneplus Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_oneplus_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_ONEPLUS_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set Oneplus mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(oneplus_mode, S_IRUGO | S_IWUSR, + mdss_fb_get_oneplus_mode, mdss_fb_set_oneplus_mode); + +static ssize_t mdss_fb_get_adaption_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int ret = 0; + int level = 0; + + level = mdss_fb_send_panel_event(mfd, + MDSS_EVENT_PANEL_GET_ADAPTION_MODE, NULL); + ret = scnprintf(buf, PAGE_SIZE, "mode = %d\n" + "0-->Adaption Mode OFF\n" + "1-->Adaption Mode ON\n", + level); + return ret; +} + +static ssize_t mdss_fb_set_adaption_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + int rc = 0; + int level = 0; + + rc = kstrtoint(buf, 10, &level); + if (rc) { + pr_err("kstrtoint failed. rc=%d\n", rc); + return rc; + } + rc = mdss_fb_send_panel_event(mfd, MDSS_EVENT_PANEL_SET_ADAPTION_MODE, + (void *)(unsigned long)level); + if (rc) + pr_err("Fail to set Adaption mode: %d\n", level); + + return count; +} + +static DEVICE_ATTR(adaption_mode, S_IRUGO | S_IWUSR, + mdss_fb_get_adaption_mode, mdss_fb_set_adaption_mode); + +/* #endif */ + + static ssize_t mdss_fb_get_persist_mode(struct device *dev, struct device_attribute *attr, char *buf) { @@ -947,6 +1198,16 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_msm_fb_panel_status.attr, &dev_attr_msm_fb_dfps_mode.attr, &dev_attr_measured_fps.attr, + &dev_attr_SRGB.attr, + + &dev_attr_Adobe_RGB.attr, + + &dev_attr_DCI_P3.attr, + + &dev_attr_night_mode.attr, + &dev_attr_adaption_mode.attr, + &dev_attr_oneplus_mode.attr, +/* #endif */ &dev_attr_msm_fb_persist_mode.attr, &dev_attr_idle_power_collapse.attr, NULL, diff --git a/drivers/video/fbdev/msm/mdss_i2c_iris.c b/drivers/video/fbdev/msm/mdss_i2c_iris.c new file mode 100644 index 000000000000..58c054b94f06 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_i2c_iris.c @@ -0,0 +1,735 @@ +/* Copyright (c) 2015, Pixelworks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mdss_debug.h" +#include "mdss_i2c_iris.h" + +#define IRIS_COMPATIBLE_NAME "pixelworks,iris2" +#define IRIS_I2C_DRIVER_NAME "iris2" + +#define I2C_DBG_TAG "iris_i2c " + +#define IRIS_I2C_DBG +#ifdef IRIS_I2C_DBG +#define iris_i2c_dbg(fmt, args...) pr_debug(I2C_DBG_TAG "[%s:%d]" fmt, __FUNCTION__, __LINE__, args) +#else +#define iris_i2c_dbg(fmt, args...) do {}while(0) +#endif + +#define MAX_READ_MSG_LEN (MAX_ADDR_COMP_LEN << 1) +#define MAX_WRITE_MSG_LEN MAX_ADDR_COMP_LEN +#define MAX_TRANSFER_MSG_LEN 31 +//#define IRIS_I2C_USE_WORKQUEUE +/* +* pixelworks extend i2c +*/ +enum { + ONE_BYTE_REG_LEN = 0x04, + TWO_BYTE_REG_LEN = 0x08, + FOUR_BYTE_REG_LEN = 0x0c, + ONE_BYTE_REG_LEN_READ = (MSMFB_IRIS_I2C_READ << 16) |ONE_BYTE_REG_LEN, + TWO_BYTE_REG_LEN_READ = (MSMFB_IRIS_I2C_READ << 16) |TWO_BYTE_REG_LEN, + FOUR_BYTE_REG_LEN_READ = (MSMFB_IRIS_I2C_READ << 16) |FOUR_BYTE_REG_LEN, + ONE_BYTE_REG_LEN_WRITE = (MSMFB_IRIS_I2C_WRITE << 16) |ONE_BYTE_REG_LEN, + TWO_BYTE_REG_LEN_WRITE = (MSMFB_IRIS_I2C_WRITE << 16) |TWO_BYTE_REG_LEN, + FOUR_BYTE_REG_LEN_WRITE = (MSMFB_IRIS_I2C_WRITE << 16) |FOUR_BYTE_REG_LEN, +}; + +enum { + DBG_I2C_READ = 0x01, + DBG_I2C_WRITE, +}; + + +/*iris i2c handle*/ +static struct i2c_client *iris_i2c_handle = NULL; +static int dbg_i2c_flag = 0; + +static int iris_i2c_rw_t(uint32_t type, struct addr_val *val, int len); + +#ifdef IRIS_I2C_USE_WORKQUEUE +typedef struct iris_i2c_mgmt_t { + uint32_t type; + int len; + struct addr_val val[MAX_ADDR_COMP_LEN]; + struct work_struct i2c_work; + struct workqueue_struct *i2c_wq; +} iris_i2c_mgmt; + +static iris_i2c_mgmt i2c_mgmt; +static void iris_i2c_work_func(struct work_struct *work) +{ + int ret = -1; + ret = iris_i2c_rw_t(i2c_mgmt.type, i2c_mgmt.val, i2c_mgmt.len); + if (ret != 0) { + pr_err("read or write is not right \n"); + } +} + +static void init_workqueue(void) { + memset(&i2c_mgmt, 0x00, sizeof(i2c_mgmt)); + i2c_mgmt.i2c_wq = create_singlethread_workqueue("i2c_wq"); + if ( !i2c_mgmt.i2c_wq) { + pr_err("could not create singlethread workqueue of i2c_wq \n"); + return; + } + + INIT_WORK(&i2c_mgmt.i2c_work, iris_i2c_work_func); +} +#endif + +int get_iris_i2c_flag(void) +{ + return dbg_i2c_flag; +} + +static ssize_t iris_dbg_i2c_write(struct file *file, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned long val; + + if (kstrtoul_from_user(buff, count, 0, &val)) + return -EFAULT; + + dbg_i2c_flag = val; + return count; +} + + +static ssize_t iris_dbg_i2c_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + int len, tot = 0; + char bp[512]; + + if (*ppos) + return 0; + + len = sizeof(bp); + tot = scnprintf(bp, len, "dbg_i2c_flag = %d\n", dbg_i2c_flag); + + if (copy_to_user(buff, bp, tot)) + return -EFAULT; + *ppos += tot; + + return tot; +} + +static const struct file_operations iris_i2c_srw_fops = { + .open = simple_open, + .write = iris_dbg_i2c_write, + .read = iris_dbg_i2c_read, +}; + +static int iris_i2c_debugfs_init(void) +{ + if (debugfs_create_file("iris_i2c_srw", 0644, NULL, NULL, &iris_i2c_srw_fops) + == NULL) { + pr_err("%s:%d""debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -EFAULT; + } + return 0; +} + +static int iris_i2c_byte_read_t(uint8_t cmd, uint32_t reg_offset, uint32_t *read_buf) +{ + struct i2c_msg msgs[2]; + int ret = -1; + int reg_len = 0; + + /* write data need cmd so need to add 0 to be comd**/ + uint8_t five_data[5] = {0,}; + uint8_t three_data[3] = {0,}; + uint8_t two_data[2] = {0,}; + + uint8_t ret_one_data[1] = {0}; + uint8_t ret_four_data[4] ={0,}; + uint8_t ret_two_data[2] = {0,}; + + struct i2c_client * client = iris_i2c_handle; + uint8_t slave_addr = 0; + uint8_t *data = NULL; + uint8_t *ret_data = NULL; + + if (!client) { + pr_err("iris i2c handle is NULL \n"); + return -EACCES; + } + + slave_addr = (client->addr &0xff); + memset(msgs, 0x00, sizeof(msgs)/sizeof(struct i2c_msg)); + + iris_i2c_dbg("reading from slave_addr=[%x] and offset=[%x]\n", + slave_addr, reg_offset); + + switch (cmd &0x0c) { + case ONE_BYTE_REG_LEN: + reg_len = 2; + two_data[0] = cmd; + two_data[1] = (reg_offset & 0xff); + data = two_data; + ret_data = ret_one_data; + break; + case TWO_BYTE_REG_LEN: + reg_len = 3; + three_data[0] = cmd; + three_data[1] = (reg_offset & 0xff); + three_data[2] = ((reg_offset >> 8) & 0xff); + data = three_data; + ret_data = ret_two_data; + break; + case FOUR_BYTE_REG_LEN: + reg_len = 5; + five_data[0] = cmd; + five_data[1] = (reg_offset & 0xff); + five_data[2] = ((reg_offset >> 8) & 0xff); + five_data[3] = ((reg_offset >> 16) & 0xff); + five_data[4] = ((reg_offset >> 24) & 0xff); + data = five_data; + ret_data = ret_four_data; + break; + } + + if (data == NULL) { + pr_err("the cmd is not right %d \n", cmd); + return -EACCES; + } + + msgs[0].addr = slave_addr; + msgs[0].flags = 0; + msgs[0].buf = data; + msgs[0].len = reg_len; + + msgs[1].addr = slave_addr & 0xff; + msgs[1].flags = I2C_M_RD; + msgs[1].buf = ret_data; + msgs[1].len = reg_len -1; + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 1) { + pr_err("%s: I2C READ FAILED=[%d]\n", __func__, ret); + return -EACCES; + } + + switch (cmd &0x0c) { + case ONE_BYTE_REG_LEN: + *read_buf = ret_data[0]; + break; + case TWO_BYTE_REG_LEN: + *read_buf = (ret_data[0] << 8) | ret_data[1]; + break; + case FOUR_BYTE_REG_LEN: + *read_buf = (ret_data[0] << 24) |(ret_data[1] << 16) + | (ret_data[2] << 8) | ret_data[3]; + break; + } + iris_i2c_dbg("i2c buf is [%x]\n",*read_buf); + return 0; +} + +int iris_i2c_byte_read(uint32_t reg_offset, uint32_t *read_buf) +{ + return iris_i2c_byte_read_t(0xcc, reg_offset, read_buf); +} + +static int iris_i2c_read_transfer( + struct i2c_adapter *adapter, struct i2c_msg *msgs, int len) +{ + int i = 0; + int pos = 0; + int ret = -1; + + for (i = 0; i < len; i++) { + pos = i << 1; + ret = i2c_transfer(adapter, &msgs[pos], 2); + if (ret < 1) { + pr_err("%s: I2C READ FAILED=[%d]\n", __func__, ret); + return -EACCES; + } + } + return 0; +} + +#if 0 +static int iris_i2c_cmd_one_read(struct addr_val * val, int len) +{ + int i = 0; + int ret = -1; + const int reg_len = 2; + const int ret_len = 1; + uint8_t cmd = 0x44; + int pos = 0; + uint8_t slave_addr = 0; + uint8_t * ret_data = NULL; + uint8_t * data = NULL; + uint8_t three_data_list[(3 * MAX_READ_MSG_LEN) >>1] = {0,}; + + struct i2c_msg msgs[MAX_READ_MSG_LEN]; + struct i2c_client * client = iris_i2c_handle; + + slave_addr = (client->addr &0xff); + memset(msgs, 0x00, sizeof(msgs)); + + for (i = 0; i < len; i++) { + pos = 3 *i; + three_data_list[pos] = cmd; + three_data_list[pos + 1] = (val[i].addr & 0xff); + data = &three_data_list[pos]; + ret_data = &data[pos + reg_len]; + + pos = i << 1; + msgs[pos].addr = slave_addr; + msgs[pos].flags = 0; + msgs[pos].buf = data; + msgs[pos].len = reg_len; + + msgs[pos + 1].addr = slave_addr; + msgs[pos + 1].flags = I2C_M_RD; + msgs[pos + 1].buf = ret_data; + msgs[pos + 1].len = ret_len; + } + + ret = iris_i2c_read_transfer(client->adapter, msgs, len); + if (ret != 0) { + return ret; + } + + for (i = 0; i < len; i++) { + pos = i * 3 + 2; + val[i].data = data[pos] ; + } + return 0; +} + +static int iris_i2c_cmd_two_read(struct addr_val * val, int len) +{ + int i = 0; + int ret = -1; + const int reg_len = 3; + const int ret_len = 2; + int pos = 0; + uint8_t cmd = 0x88; + uint8_t slave_addr = 0; + uint8_t * ret_data = NULL; + uint8_t * data = NULL; + + uint8_t five_data_list[(5 * MAX_READ_MSG_LEN) >>1] = {0,}; + + struct i2c_msg msgs[MAX_READ_MSG_LEN]; + struct i2c_client * client = iris_i2c_handle; + + slave_addr = (client->addr &0xff); + memset(msgs, 0x00, sizeof(msgs)); + + for (i = 0; i < len; i++) { + pos = 5 * i; + + five_data_list[pos] = cmd; + five_data_list[pos + 1] = (val[i].addr & 0xff); + five_data_list[pos + 2] = ((val[i].addr >> 8) & 0xff); + data = &five_data_list[pos]; + ret_data = &five_data_list[pos +reg_len]; + + pos = i << 1; + msgs[pos].addr = slave_addr; + msgs[pos].flags = 0; + msgs[pos].buf = data; + msgs[pos].len = reg_len; + + msgs[pos + 1].addr = slave_addr; + msgs[pos + 1].flags = I2C_M_RD; + msgs[pos + 1].buf = ret_data; + msgs[pos + 1].len = ret_len; + } + + ret = iris_i2c_read_transfer(client->adapter, msgs, len); + if (ret != 0) { + return ret; + } + + for (i = 0; i < len; i++) { + pos = i * 5 + 3; + pos = (i << 1); + val[i].data = (five_data_list[pos] << 8) | five_data_list[1 + pos]; + } + return 0; +} +#endif + +static int iris_i2c_cmd_four_read(struct addr_val * val, int len) +{ + int i = 0; + int ret = -1; + int pos = 0; + const int reg_len = 5; + const int ret_len = 4; + uint8_t cmd = 0xcc; + uint8_t slave_addr = 0; + uint8_t *data = NULL; + uint8_t * ret_data = NULL; + + /*for ret value need to be N * len + * N is cmd + val+ ret (1+1+1,1+2+2,1+4+4)*/ + uint8_t nine_data_list[(9 * MAX_READ_MSG_LEN) >>1] = {0,}; + + struct i2c_msg msgs[MAX_READ_MSG_LEN]; + struct i2c_client * client = iris_i2c_handle; + + slave_addr = (client->addr &0xff); + memset(msgs, 0x00, sizeof(msgs)); + + for (i = 0; i < len; i++) { + pos = 9 * i; + nine_data_list[pos] = cmd; + nine_data_list[pos + 1] = (val[i].addr & 0xff); + nine_data_list[pos + 2] = ((val[i].addr >> 8) & 0xff); + nine_data_list[pos + 3] = ((val[i].addr >> 16) & 0xff); + nine_data_list[pos + 4] = ((val[i].addr >> 24) & 0xff); + data = &nine_data_list[pos]; + ret_data = &nine_data_list[pos + reg_len]; + + pos = i << 1; + msgs[pos].addr = slave_addr; + msgs[pos].flags = 0; + msgs[pos].buf = data; + msgs[pos].len = reg_len; + + msgs[pos + 1].addr = slave_addr; + msgs[pos + 1].flags = I2C_M_RD; + msgs[pos + 1].buf = ret_data; + msgs[pos + 1].len = ret_len; + } + + ret = iris_i2c_read_transfer(client->adapter, msgs, len); + if (ret != 0) { + return ret; + } + + for (i = 0; i < len; i++) { + pos = 9 * i + 5; + val[i].data = (nine_data_list[pos] << 24) |(nine_data_list[pos + 1] << 16) + | (nine_data_list[pos + 2] << 8) | nine_data_list[pos + 3]; + } + + return 0; +} + +static int iris_i2c_byte_write_t(uint8_t cmd, uint32_t reg_offset, uint32_t value) +{ + struct i2c_msg msgs[1]; + + /* write data need cmd so need to add 0 to be comd**/ + uint8_t nine_data[9] = {0,}; + uint8_t five_data[5] = {0,}; + uint8_t three_data[3] ={0,}; + + uint8_t *data = NULL; + int status = -EACCES; + int reg_len = 0; + struct i2c_client * client = iris_i2c_handle; + uint8_t slave_addr = 0; + + if (!client) { + pr_err("iris i2c handle is NULL \n"); + return -EACCES; + } + + slave_addr = (client->addr & 0xff); + memset(msgs, 0x00, sizeof(msgs)/sizeof(struct i2c_msg)); + + iris_i2c_dbg("writing from slave_addr=[%x] and offset=[%x] val = [%x]\n", + slave_addr, reg_offset, value); + + switch (cmd &0x0c) { + case ONE_BYTE_REG_LEN: + reg_len = 3; + three_data[0] = cmd; + three_data[1] = (reg_offset & 0xff); + three_data[2] = (value & 0xff); + data = three_data; + break; + case TWO_BYTE_REG_LEN: + reg_len = 5; + five_data[0] = cmd; + five_data[1] = (reg_offset &0xff); + five_data[2] = ((reg_offset >>8) & 0xff); + /*data and address in reverse direction */ + five_data[3] = (value & 0xff); + five_data[4] = ((value >> 8) & 0xff); + data = five_data; + break; + case FOUR_BYTE_REG_LEN: + reg_len = 9; + nine_data[0] = cmd; + nine_data[1] = (reg_offset & 0xff); + nine_data[2] = ((reg_offset >> 8) & 0xff); + nine_data[3] = ((reg_offset >> 16) & 0xff); + nine_data[4] = ((reg_offset >> 24) & 0xff); + nine_data[5] = ((value >> 24) &0xff); + nine_data[6] = ((value >> 16) &0xff); + nine_data[7] = ((value >> 8) &0xff); + nine_data[8] = (value & 0xff); + data = nine_data; + break; + } + + if (data == NULL) { + pr_err("cmd is not all right %x\n", cmd ); + return -EACCES; + } + + msgs[0].addr = slave_addr; + msgs[0].flags = 0; + msgs[0].len = reg_len; + msgs[0].buf = data; + + status = i2c_transfer(client->adapter, msgs, 1); + if (status < 1) { + pr_err("I2C WRITE FAILED=[%d]\n", status); + return -EACCES; + } + iris_i2c_dbg("I2C write status=%x\n", status); + + return status; +} + + +int iris_i2c_byte_write(uint32_t reg_offset, uint32_t value) +{ + uint8_t cmd = 0xcc; + return iris_i2c_byte_write_t(cmd, reg_offset, value); +} + +static int iris_i2c_cmd_four_write(struct addr_val * val, int len) +{ + int i = 0; + int ret = -1; + int pos = 0; + const int reg_len = 9; + const uint8_t cmd = 0xcc; + uint8_t slave_addr = 0; + uint8_t *data = NULL; + int32_t mult_val = 0; + int32_t pos_val = 0; + //uint8_t trace_cmd[128] = {0,}; + //int func_name_len; + + /*for ret value need to be N * len + * N is cmd + addr+ val (1+1+1,1+2+2,1+4+4)*/ + uint8_t nine_data_list[9 * MAX_WRITE_MSG_LEN] = {0,}; + + struct i2c_msg msgs[MAX_WRITE_MSG_LEN]; + struct i2c_client * client = iris_i2c_handle; + + slave_addr = (client->addr &0xff); + memset(msgs, 0x00, sizeof(msgs)); + + for (i = 0; i < len; i++) { + pos = reg_len * i; + nine_data_list[pos] = cmd; + nine_data_list[pos + 1] = (val[i].addr & 0xff); + nine_data_list[pos + 2] = ((val[i].addr >> 8) & 0xff); + nine_data_list[pos + 3] = ((val[i].addr >> 16) & 0xff); + nine_data_list[pos + 4] = ((val[i].addr >> 24) & 0xff); + nine_data_list[pos + 5] = ((val[i].data >> 24) &0xff); + nine_data_list[pos + 6] = ((val[i].data >> 16) &0xff); + nine_data_list[pos + 7] = ((val[i].data >> 8) &0xff); + nine_data_list[pos + 8] = (val[i].data & 0xff); + + data = &nine_data_list[pos]; + + msgs[i].addr = slave_addr; + msgs[i].flags = 0; + msgs[i].buf = data; + msgs[i].len = reg_len; + } + + //func_name_len = sprintf(trace_cmd, "%s_", __func__ ); + //sprintf(&trace_cmd[func_name_len], "%d", len ); + //ATRACE_BEGIN(trace_cmd); + + /*according to I2C_MSM_BAM_CONS_SZ in i2c_msm_v2.h + the write msg should be less than 32 */ + if (len <= MAX_TRANSFER_MSG_LEN) { + ret = i2c_transfer(client->adapter, msgs, len); + if (ret < 1) { + return ret; + } + } else { + mult_val = (len / MAX_TRANSFER_MSG_LEN); + pos_val = len - (mult_val * MAX_TRANSFER_MSG_LEN); + for (i = 0; i < mult_val; i++) { + ret = i2c_transfer(client->adapter, + &msgs[i * MAX_TRANSFER_MSG_LEN], MAX_TRANSFER_MSG_LEN); + if (ret < 1) { + return ret; + } + } + + ret = i2c_transfer(client->adapter, + &msgs[i * MAX_TRANSFER_MSG_LEN], pos_val); + if (ret < 1) { + return ret; + } + } + + //ATRACE_END(trace_cmd); + return 0; +} + +static int iris_i2c_rw_t(uint32_t type, struct addr_val *val, int len) +{ + int ret = -1; + + switch (type) { +#if 0 + case ONE_BYTE_REG_LEN_READ: + ret = iris_i2c_cmd_one_read(val, len); + break; + case TWO_BYTE_REG_LEN_READ: + ret = iris_i2c_cmd_two_read(val, len); + break; +#endif + case FOUR_BYTE_REG_LEN_READ: + ret = iris_i2c_cmd_four_read(val, len); + break; + case FOUR_BYTE_REG_LEN_WRITE: + ret = iris_i2c_cmd_four_write(val, len); + break; + default: + pr_err("can not identify the cmd = %x\n", type); + return -EINVAL; + } + return ret; +} + +/*currently we use four byte*/ +static int iris_i2c_rw(uint32_t type, struct addr_val *val, int len) +{ + struct i2c_client * client = iris_i2c_handle; + + if (!client) { + pr_err("iris i2c handle is NULL \n"); + return -EACCES; + } + + if (!val || len == 0) { + pr_err("the return buf = %p or len = %d \n", + val, len); + return -EINVAL; + } + + if (MSMFB_IRIS_I2C_READ == ((type >> 16)& 0xffff) + && len > MAX_READ_MSG_LEN){ + pr_err("the len is two long for read len = %d " + "MAX_READ_MSG_LEN = %d \n", len, MAX_READ_MSG_LEN); + return -EINVAL; + } else if (MSMFB_IRIS_I2C_WRITE == ((type >> 16)& 0xffff) + && len > MAX_WRITE_MSG_LEN) { + pr_err("the len is two long for read len = %d " + "MAX_READ_MSG_LEN = %d \n", len, MAX_WRITE_MSG_LEN); + return -EINVAL; + } +#ifdef IRIS_I2C_USE_WORKQUEUE + if (MSMFB_IRIS_I2C_READ == ((type >> 16)& 0xffff)) { + return iris_i2c_rw_t(type, val, len); + } else { + //val is local variable should not be call + memcpy(i2c_mgmt.val, val, len * sizeof(i2c_mgmt.val[0])); + i2c_mgmt.len = len; + i2c_mgmt.type = type; + queue_work(i2c_mgmt.i2c_wq, &i2c_mgmt.i2c_work); + return 0; + } +#else + return iris_i2c_rw_t(type, val, len); +#endif +} + +/**now we support four byte read and write*/ +int iris_i2c_read(struct addr_val *val, int len) { + return iris_i2c_rw(FOUR_BYTE_REG_LEN_READ, val, len); +} + +int iris_i2c_write(struct addr_val *val, int len) { + return iris_i2c_rw(FOUR_BYTE_REG_LEN_WRITE, val, len); +} + +static int iris_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + iris_i2c_handle = client; + return 0; +} + +static int iris_i2c_remove(struct i2c_client *client) +{ + iris_i2c_handle = NULL; + return 0; +} + +static const struct i2c_device_id iris_i2c_id_table[] = { + {IRIS_I2C_DRIVER_NAME, 0}, + {}, +}; + + +static struct of_device_id iris_match_table[] = { + {.compatible = IRIS_COMPATIBLE_NAME,}, + { }, +}; + +static struct i2c_driver plx_i2c_driver = { + .driver = { + .name = IRIS_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = iris_match_table, + }, + .probe = iris_i2c_probe, + .remove = iris_i2c_remove, + .id_table = iris_i2c_id_table, +}; + + +int iris_i2c_bus_init(void) +{ + i2c_add_driver(&plx_i2c_driver); + iris_i2c_debugfs_init(); +#ifdef IRIS_I2C_USE_WORKQUEUE + init_workqueue(); +#endif + return 0; +} + +void iris_i2c_bus_exit(void) +{ + i2c_del_driver(&plx_i2c_driver); + iris_i2c_remove(iris_i2c_handle); +#ifdef IRIS_I2C_USE_WORKQUEUE + destroy_workqueue(i2c_mgmt.i2c_wq); +#endif + return; +} diff --git a/drivers/video/fbdev/msm/mdss_i2c_iris.h b/drivers/video/fbdev/msm/mdss_i2c_iris.h new file mode 100644 index 000000000000..5be4f886e561 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_i2c_iris.h @@ -0,0 +1,16 @@ +#ifndef MDSS_I2C_IRIS_H +#define MDSS_I2C_IRIS_H + +#include +#include +#include + +int iris_i2c_byte_read(uint32_t reg_offset, uint32_t *read_buf); +int iris_i2c_byte_write(uint32_t reg_offset, uint32_t value); +int iris_i2c_read(struct addr_val *val, int len); +int iris_i2c_write(struct addr_val *val, int len); +int iris_i2c_bus_init(void); +void iris_i2c_bus_exit(void); +int get_iris_i2c_flag(void); + +#endif diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index acac672662c1..3340496100d5 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -305,6 +305,19 @@ enum mdss_intf_events { MDSS_EVENT_DEEP_COLOR, MDSS_EVENT_DISABLE_PANEL, MDSS_EVENT_UPDATE_PANEL_PPM, + + MDSS_EVENT_PANEL_SET_SRGB_MODE, + MDSS_EVENT_PANEL_GET_SRGB_MODE, + MDSS_EVENT_PANEL_SET_ADOBE_RGB_MODE, + MDSS_EVENT_PANEL_GET_ADOBE_RGB_MODE, + MDSS_EVENT_PANEL_SET_DCI_P3_MODE, + MDSS_EVENT_PANEL_GET_DCI_P3_MODE, + MDSS_EVENT_PANEL_SET_NIGHT_MODE, + MDSS_EVENT_PANEL_GET_NIGHT_MODE, + MDSS_EVENT_PANEL_SET_ONEPLUS_MODE, + MDSS_EVENT_PANEL_GET_ONEPLUS_MODE, + MDSS_EVENT_PANEL_SET_ADAPTION_MODE, + MDSS_EVENT_PANEL_GET_ADAPTION_MODE, MDSS_EVENT_DSI_TIMING_DB_CTRL, MDSS_EVENT_AVR_MODE, MDSS_EVENT_REGISTER_CLAMP_HANDLER, diff --git a/fs/Kconfig b/fs/Kconfig index 89ddd182f568..a61f891867e1 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -105,6 +105,8 @@ menu "DOS/FAT/NT Filesystems" source "fs/fat/Kconfig" source "fs/ntfs/Kconfig" +source "fs/exfat/Kconfig" + endmenu endif # BLOCK diff --git a/fs/Makefile b/fs/Makefile index 4644db462ba9..1ca499c6857e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -128,3 +128,5 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ + +obj-$(CONFIG_EXFAT_FS) += exfat/ diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig new file mode 100755 index 000000000000..78b32aa2ca19 --- /dev/null +++ b/fs/exfat/Kconfig @@ -0,0 +1,39 @@ +config EXFAT_FS + tristate "exFAT fs support" + select NLS + help + This adds support for the exFAT file system. + +config EXFAT_DISCARD + bool "enable discard support" + depends on EXFAT_FS + default y + +config EXFAT_DELAYED_SYNC + bool "enable delayed sync" + depends on EXFAT_FS + default n + +config EXFAT_KERNEL_DEBUG + bool "enable kernel debug features via ioctl" + depends on EXFAT_FS + default n + +config EXFAT_DEBUG_MSG + bool "print debug messages" + depends on EXFAT_FS + default n + +config EXFAT_DEFAULT_CODEPAGE + int "Default codepage for exFAT" + default 437 + depends on EXFAT_FS + help + This option should be set to the codepage of your exFAT filesystems. + +config EXFAT_DEFAULT_IOCHARSET + string "Default iocharset for exFAT" + default "utf8" + depends on EXFAT_FS + help + Set this to the default input/output character set you'd like exFAT to use. diff --git a/fs/exfat/LICENSE b/fs/exfat/LICENSE new file mode 100755 index 000000000000..d159169d1050 --- /dev/null +++ b/fs/exfat/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile new file mode 100755 index 000000000000..fb2be9dc5a93 --- /dev/null +++ b/fs/exfat/Makefile @@ -0,0 +1,55 @@ +# +# Makefile for Linux FAT12/FAT16/FAT32(VFAT)/FAT64(ExFAT) filesystem driver. +# + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-$(CONFIG_EXFAT_FS) += exfat.o + +exfat-objs := exfat_core.o exfat_super.o exfat_api.o exfat_blkdev.o exfat_cache.o \ + exfat_data.o exfat_bitmap.o exfat_nls.o exfat_oal.o exfat_upcase.o + +else +# external module build + +EXTRA_FLAGS += -I$(PWD) + +# +# KDIR is a path to a directory containing kernel source. +# It can be specified on the command line passed to make to enable the module to +# be built and installed for a kernel other than the one currently running. +# By default it is the path to the symbolic link created when +# the current kernel's modules were installed, but +# any valid path to the directory in which the target kernel's source is located +# can be provided on the command line. +# +KDIR := /lib/modules/$(shell uname -r)/build +MDIR := /lib/modules/$(shell uname -r) +PWD := $(shell pwd) +KREL := $(shell cd ${KDIR} && make -s kernelrelease) +PWD := $(shell pwd) + +export CONFIG_EXFAT_FS := m + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +help: + $(MAKE) -C $(KDIR) M=$(PWD) help + +install: exfat.ko + rm -f ${MDIR}/kernel/fs/exfat/exfat.ko + install -m644 -b -D exfat.ko ${MDIR}/kernel/fs/exfat/exfat.ko + depmod -aq + +uninstall: + rm -rf ${MDIR}/kernel/fs/exfat + depmod -aq + +endif + +.PHONY : all clean install uninstall diff --git a/fs/exfat/README.md b/fs/exfat/README.md new file mode 100755 index 000000000000..3e1887ff5217 --- /dev/null +++ b/fs/exfat/README.md @@ -0,0 +1,74 @@ +exfat-nofuse +============ + +Linux non-fuse read/write kernel driver for the exFAT, FAT12, FAT16 and vfat (FAT32) file systems.
+Originally ported from Android kernel v3.0. + +Kudos to ksv1986 for the mutex patch!
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.
+
+Big thanks to lqs for completing the driver!
+Big thanks to benpicco for fixing 3.11.y compatibility! + + +Special thanks to github user AndreiLux for spreading the word about the leak!
+ + +Installing as a stand-alone module: +==================================== + + make + sudo make install + +To load the driver manually, run this as root: + + modprobe exfat + +You may also specify custom toolchains by using CROSS_COMPILE flag, in my case: +>CROSS_COMPILE=../dorimanx-SG2-I9100-Kernel/android-toolchain/bin/arm-eabi- + +Installing as a part of the kernel: +====================================== + +Let's take [linux] as the path to your kernel source dir... + + cd [linux] + cp -rvf exfat-nofuse [linux]/fs/exfat + +edit [linux]/fs/Kconfig +``` + menu "DOS/FAT/NT Filesystems" + + source "fs/fat/Kconfig" + +source "fs/exfat/Kconfig" + source "fs/ntfs/Kconfig" + endmenu +``` + + +edit [linux]/fs/Makefile +``` + obj-$(CONFIG_FAT_FS) += fat/ + +obj-$(CONFIG_EXFAT_FS) += exfat/ + obj-$(CONFIG_BFS_FS) += bfs/ +``` + + cd [linux] + make menuconfig + +Go to: +> File systems > DOS/FAT/NT +> check exfat as MODULE (M) +> (437) Default codepage for exFAT +> (utf8) Default iocharset for exFAT + +> ESC to main menu +> Save an Alternate Configuration File +> ESC ESC + +build your kernel + +Have fun. + +Free Software for the Free Minds! +================================= diff --git a/fs/exfat/dkms.conf b/fs/exfat/dkms.conf new file mode 100755 index 000000000000..e98dba312734 --- /dev/null +++ b/fs/exfat/dkms.conf @@ -0,0 +1,7 @@ +PACKAGE_NAME="exfat" +PACKAGE_VERSION="1.2.8" +MAKE="KDIR=/lib/modules/$kernelver/build make" +CLEAN="make clean" +BUILT_MODULE_NAME[0]="exfat" +AUTOINSTALL="yes" +DEST_MODULE_LOCATION="/extra" diff --git a/fs/exfat/exfat_api.c b/fs/exfat/exfat_api.c new file mode 100755 index 000000000000..32b29f0dd8d9 --- /dev/null +++ b/fs/exfat/exfat_api.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.c */ +/* PURPOSE : exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern struct semaphore z_sem; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Global Function Definitions */ +/* - All functions for global use have same return value format, */ +/* that is, FFS_SUCCESS on success and several FS error code on */ +/* various error condition. */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* exFAT Filesystem Init & Exit Functions */ +/*----------------------------------------------------------------------*/ + +int FsInit(void) +{ + return ffsInit(); +} + +int FsShutdown(void) +{ + return ffsShutdown(); +} + +/*----------------------------------------------------------------------*/ +/* Volume Management Functions */ +/*----------------------------------------------------------------------*/ + +/* FsMountVol : mount the file system volume */ +int FsMountVol(struct super_block *sb) +{ + int err; + + sm_P(&z_sem); + + err = buf_init(sb); + if (!err) + err = ffsMountVol(sb); + else + buf_shutdown(sb); + + sm_V(&z_sem); + + return err; +} /* end of FsMountVol */ + +/* FsUmountVol : unmount the file system volume */ +int FsUmountVol(struct super_block *sb) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&z_sem); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsUmountVol(sb); + buf_shutdown(sb); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + sm_V(&z_sem); + + return err; +} /* end of FsUmountVol */ + +/* FsGetVolInfo : get the information of a file system volume */ +int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (info == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsGetVolInfo(sb, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsGetVolInfo */ + +/* FsSyncVol : synchronize a file system volume */ +int FsSyncVol(struct super_block *sb, int do_sync) +{ + int err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsSyncVol(sb, do_sync); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsSyncVol */ + + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateFile : create a file */ +int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsLookupFile(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsLookupFile */ + +/* FsCreateFile : create a file */ +int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsCreateFile(inode, path, mode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsCreateFile */ + +int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* check the validity of pointer parameters */ + if (buffer == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsReadFile(inode, fid, buffer, count, rcount); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadFile */ + +int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* check the validity of pointer parameters */ + if (buffer == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsWriteFile(inode, fid, buffer, count, wcount); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsWriteFile */ + +/* FsTruncateFile : resize the file length */ +int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + DPRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); + + err = ffsTruncateFile(inode, old_size, new_size); + + DPRINTK("FsTruncateFile exitted (%d)\n", err); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsTruncateFile */ + +/* FsMoveFile : move(rename) a old file into a new file */ +int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + int err; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsMoveFile */ + +/* FsRemoveFile : remove a file */ +int FsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsRemoveFile(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsRemoveFile */ + +/* FsSetAttr : set the attribute of a given file */ +int FsSetAttr(struct inode *inode, u32 attr) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsSetAttr(inode, attr); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsSetAttr */ + +/* FsReadStat : get the information of a given file */ +int FsReadStat(struct inode *inode, DIR_ENTRY_T *info) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsGetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadStat */ + +/* FsWriteStat : set the information of a given file */ +int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + DPRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); + + err = ffsSetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + DPRINTK("FsWriteStat exited (%d)\n", err); + + return err; +} /* end of FsWriteStat */ + +/* FsMapCluster : return the cluster number in the given cluster offset */ +int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (clu == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsMapCluster(inode, clu_offset, clu); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateDir : create(make) a directory */ +int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsCreateDir(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsCreateDir */ + +/* FsReadDir : read a directory entry from the opened directory */ +int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (dir_entry == NULL) + return FFS_ERROR; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsReadDir(inode, dir_entry); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsReadDir */ + +/* FsRemoveDir : remove a directory */ +int FsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + int err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) + return FFS_INVALIDFID; + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + err = ffsRemoveDir(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return err; +} /* end of FsRemoveDir */ + +EXPORT_SYMBOL(FsMountVol); +EXPORT_SYMBOL(FsUmountVol); +EXPORT_SYMBOL(FsGetVolInfo); +EXPORT_SYMBOL(FsSyncVol); +EXPORT_SYMBOL(FsLookupFile); +EXPORT_SYMBOL(FsCreateFile); +EXPORT_SYMBOL(FsReadFile); +EXPORT_SYMBOL(FsWriteFile); +EXPORT_SYMBOL(FsTruncateFile); +EXPORT_SYMBOL(FsMoveFile); +EXPORT_SYMBOL(FsRemoveFile); +EXPORT_SYMBOL(FsSetAttr); +EXPORT_SYMBOL(FsReadStat); +EXPORT_SYMBOL(FsWriteStat); +EXPORT_SYMBOL(FsMapCluster); +EXPORT_SYMBOL(FsCreateDir); +EXPORT_SYMBOL(FsReadDir); +EXPORT_SYMBOL(FsRemoveDir); + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG +/* FsReleaseCache: Release FAT & buf cache */ +int FsReleaseCache(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&p_fs->v_sem); + + FAT_release_all(sb); + buf_release_all(sb); + + /* release the lock for file system critical section */ + sm_V(&p_fs->v_sem); + + return 0; +} +/* FsReleaseCache */ + +EXPORT_SYMBOL(FsReleaseCache); +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ diff --git a/fs/exfat/exfat_api.h b/fs/exfat/exfat_api.h new file mode 100755 index 000000000000..84bdf612a1e6 --- /dev/null +++ b/fs/exfat/exfat_api.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.h */ +/* PURPOSE : Header File for exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_API_H +#define _EXFAT_API_H + +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define EXFAT_SUPER_MAGIC (0x2011BAB0L) +#define EXFAT_ROOT_INO 1 + +/* FAT types */ +#define FAT12 0x01 /* FAT12 */ +#define FAT16 0x0E /* Win95 FAT16 (LBA) */ +#define FAT32 0x0C /* Win95 FAT32 (LBA) */ +#define EXFAT 0x07 /* exFAT */ + +/* file name lengths */ +#define MAX_CHARSET_SIZE 3 /* max size of multi-byte character */ +#define MAX_PATH_DEPTH 15 /* max depth of path name */ +#define MAX_NAME_LENGTH 256 /* max len of file name including NULL */ +#define MAX_PATH_LENGTH 260 /* max len of path name including NULL */ +#define DOS_NAME_LENGTH 11 /* DOS file name length excluding NULL */ +#define DOS_PATH_LENGTH 80 /* DOS path name length excluding NULL */ + +/* file attributes */ +#define ATTR_NORMAL 0x0000 +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_SYMLINK 0x0040 +#define ATTR_EXTEND 0x000F +#define ATTR_RWMASK 0x007E + +/* file creation modes */ +#define FM_REGULAR 0x00 +#define FM_SYMLINK 0x40 + +/* return values */ +#define FFS_SUCCESS 0 +#define FFS_MEDIAERR 1 +#define FFS_FORMATERR 2 +#define FFS_MOUNTED 3 +#define FFS_NOTMOUNTED 4 +#define FFS_ALIGNMENTERR 5 +#define FFS_SEMAPHOREERR 6 +#define FFS_INVALIDPATH 7 +#define FFS_INVALIDFID 8 +#define FFS_NOTFOUND 9 +#define FFS_FILEEXIST 10 +#define FFS_PERMISSIONERR 11 +#define FFS_NOTOPENED 12 +#define FFS_MAXOPENED 13 +#define FFS_FULL 14 +#define FFS_EOF 15 +#define FFS_DIRBUSY 16 +#define FFS_MEMORYERR 17 +#define FFS_NAMETOOLONG 18 +#define FFS_ERROR 19 + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct { + u16 Year; + u16 Month; + u16 Day; + u16 Hour; + u16 Minute; + u16 Second; + u16 MilliSecond; +} DATE_TIME_T; + +typedef struct { + u32 Offset; /* start sector number of the partition */ + u32 Size; /* in sectors */ +} PART_INFO_T; + +typedef struct { + u32 SecSize; /* sector size in bytes */ + u32 DevSize; /* block device size in sectors */ +} DEV_INFO_T; + +typedef struct { + u32 FatType; + u32 ClusterSize; + u32 NumClusters; + u32 FreeClusters; + u32 UsedClusters; +} VOL_INFO_T; + +/* directory structure */ +typedef struct { + u32 dir; + s32 size; + u8 flags; +} CHAIN_T; + +/* file id structure */ +typedef struct { + CHAIN_T dir; + s32 entry; + u32 type; + u32 attr; + u32 start_clu; + u64 size; + u8 flags; + s64 rwoffset; + s32 hint_last_off; + u32 hint_last_clu; +} FILE_ID_T; + +typedef struct { + char Name[MAX_NAME_LENGTH * MAX_CHARSET_SIZE]; + char ShortName[DOS_NAME_LENGTH + 2]; /* used only for FAT12/16/32, not used for exFAT */ + u32 Attr; + u64 Size; + u32 NumSubdirs; + DATE_TIME_T CreateTimestamp; + DATE_TIME_T ModifyTimestamp; + DATE_TIME_T AccessTimestamp; +} DIR_ENTRY_T; + +/*======================================================================*/ +/* */ +/* API FUNCTION DECLARATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ + int FsInit(void); + int FsShutdown(void); + +/* volume management functions */ + int FsMountVol(struct super_block *sb); + int FsUmountVol(struct super_block *sb); + int FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); + int FsSyncVol(struct super_block *sb, int do_sync); + +/* file management functions */ + int FsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); + int FsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); + int FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); + int FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); + int FsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); + int FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); + int FsRemoveFile(struct inode *inode, FILE_ID_T *fid); + int FsSetAttr(struct inode *inode, u32 attr); + int FsReadStat(struct inode *inode, DIR_ENTRY_T *info); + int FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); + int FsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); + +/* directory management functions */ + int FsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); + int FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); + int FsRemoveDir(struct inode *inode, FILE_ID_T *fid); + +/* debug functions */ +s32 FsReleaseCache(struct super_block *sb); + +#endif /* _EXFAT_API_H */ diff --git a/fs/exfat/exfat_bitmap.c b/fs/exfat/exfat_bitmap.c new file mode 100755 index 000000000000..b0672dd073fc --- /dev/null +++ b/fs/exfat/exfat_bitmap.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.c */ +/* PURPOSE : exFAT Miscellaneous Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_bitmap.h" + +/*----------------------------------------------------------------------*/ +/* Bitmap Manipulation Functions */ +/*----------------------------------------------------------------------*/ + +#define BITMAP_LOC(v) ((v) >> 3) +#define BITMAP_SHIFT(v) ((v) & 0x07) + +s32 exfat_bitmap_test(u8 *bitmap, int i) +{ + u8 data; + + data = bitmap[BITMAP_LOC(i)]; + if ((data >> BITMAP_SHIFT(i)) & 0x01) + return 1; + return 0; +} /* end of Bitmap_test */ + +void exfat_bitmap_set(u8 *bitmap, int i) +{ + bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_set */ + +void exfat_bitmap_clear(u8 *bitmap, int i) +{ + bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_clear */ diff --git a/fs/exfat/exfat_bitmap.h b/fs/exfat/exfat_bitmap.h new file mode 100755 index 000000000000..4f482c7b28cc --- /dev/null +++ b/fs/exfat/exfat_bitmap.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.h */ +/* PURPOSE : Header File for exFAT Global Definitions & Misc Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_BITMAP_H +#define _EXFAT_BITMAP_H + +#include + +/*======================================================================*/ +/* */ +/* LIBRARY FUNCTION DECLARATIONS -- OTHER UTILITY FUNCTIONS */ +/* (DO NOT CHANGE THIS PART !!) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Bitmap Manipulation Functions */ +/*----------------------------------------------------------------------*/ + +s32 exfat_bitmap_test(u8 *bitmap, int i); +void exfat_bitmap_set(u8 *bitmap, int i); +void exfat_bitmap_clear(u8 *bitmpa, int i); + +#endif /* _EXFAT_BITMAP_H */ diff --git a/fs/exfat/exfat_blkdev.c b/fs/exfat/exfat_blkdev.c new file mode 100755 index 000000000000..02fa4fe18eca --- /dev/null +++ b/fs/exfat/exfat_blkdev.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.c */ +/* PURPOSE : exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include "exfat_config.h" +#include "exfat_blkdev.h" +#include "exfat_data.h" +#include "exfat_api.h" +#include "exfat_super.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Function Definitions */ +/*======================================================================*/ + +s32 bdev_init(void) +{ + return FFS_SUCCESS; +} + +s32 bdev_shutdown(void) +{ + return FFS_SUCCESS; +} + +s32 bdev_open(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bd->opened) + return FFS_SUCCESS; + + p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); + p_bd->sector_size_bits = ilog2(p_bd->sector_size); + p_bd->sector_size_mask = p_bd->sector_size - 1; + p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; + + p_bd->opened = TRUE; + + return FFS_SUCCESS; +} + +s32 bdev_close(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (!p_bd->opened) + return FFS_SUCCESS; + + p_bd->opened = FALSE; + return FFS_SUCCESS; +} + +s32 bdev_read(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + if (*bh) + __brelse(*bh); + + if (read) + *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + else + *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + + if (*bh) + return FFS_SUCCESS; + + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return FFS_MEDIAERR; +} + +s32 bdev_write(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync) +{ + s32 count; + struct buffer_head *bh2; + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + if (secno == bh->b_blocknr) { + lock_buffer(bh); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + if (sync && (sync_dirty_buffer(bh) != 0)) + return FFS_MEDIAERR; + } else { + count = num_secs << p_bd->sector_size_bits; + + bh2 = __getblk(sb->s_bdev, secno, count); + + if (bh2 == NULL) + goto no_bh; + + lock_buffer(bh2); + memcpy(bh2->b_data, bh->b_data, count); + set_buffer_uptodate(bh2); + mark_buffer_dirty(bh2); + unlock_buffer(bh2); + if (sync && (sync_dirty_buffer(bh2) != 0)) { + __brelse(bh2); + goto no_bh; + } + __brelse(bh2); + } + + return FFS_SUCCESS; + +no_bh: + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return FFS_MEDIAERR; +} + +s32 bdev_sync(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) + return FFS_MEDIAERR; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + if (!p_bd->opened) + return FFS_MEDIAERR; + + return sync_blockdev(sb->s_bdev); +} diff --git a/fs/exfat/exfat_blkdev.h b/fs/exfat/exfat_blkdev.h new file mode 100755 index 000000000000..169d25d46bf4 --- /dev/null +++ b/fs/exfat/exfat_blkdev.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.h */ +/* PURPOSE : Header File for exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_BLKDEV_H +#define _EXFAT_BLKDEV_H + +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Non-Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct __BD_INFO_T { + s32 sector_size; /* in bytes */ + s32 sector_size_bits; + s32 sector_size_mask; + s32 num_sectors; /* total number of sectors in this block device */ + bool opened; /* opened or not */ +} BD_INFO_T; + +/*----------------------------------------------------------------------*/ +/* External Variable Declarations */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 bdev_init(void); +s32 bdev_shutdown(void); +s32 bdev_open(struct super_block *sb); +s32 bdev_close(struct super_block *sb); +s32 bdev_read(struct super_block *sb, u32 secno, struct buffer_head **bh, u32 num_secs, s32 read); +s32 bdev_write(struct super_block *sb, u32 secno, struct buffer_head *bh, u32 num_secs, s32 sync); +s32 bdev_sync(struct super_block *sb); + +#endif /* _EXFAT_BLKDEV_H */ diff --git a/fs/exfat/exfat_cache.c b/fs/exfat/exfat_cache.c new file mode 100755 index 000000000000..e6ca88b8e8d7 --- /dev/null +++ b/fs/exfat/exfat_cache.c @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.c */ +/* PURPOSE : exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" + +#include "exfat_cache.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +#define sm_P(s) +#define sm_V(s) + +static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content); +static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content); + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec); +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec); +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void FAT_cache_remove_hash(BUF_CACHE_T *bp); + +static u8 *__buf_getblk(struct super_block *sb, u32 sec); + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec); +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec); +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void buf_cache_remove_hash(BUF_CACHE_T *bp); + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); + +/*======================================================================*/ +/* Cache Initialization Functions */ +/*======================================================================*/ + +s32 buf_init(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + int i; + + /* LRU list */ + p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + p_fs->FAT_cache_array[i].drv = -1; + p_fs->FAT_cache_array[i].sec = ~0; + p_fs->FAT_cache_array[i].flag = 0; + p_fs->FAT_cache_array[i].buf_bh = NULL; + p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; + push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); + } + + p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; + + for (i = 0; i < BUF_CACHE_SIZE; i++) { + p_fs->buf_cache_array[i].drv = -1; + p_fs->buf_cache_array[i].sec = ~0; + p_fs->buf_cache_array[i].flag = 0; + p_fs->buf_cache_array[i].buf_bh = NULL; + p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; + push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); + } + + /* HASH list */ + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { + p_fs->FAT_cache_hash_list[i].drv = -1; + p_fs->FAT_cache_hash_list[i].sec = ~0; + p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); + } + + for (i = 0; i < FAT_CACHE_SIZE; i++) + FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); + + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { + p_fs->buf_cache_hash_list[i].drv = -1; + p_fs->buf_cache_hash_list[i].sec = ~0; + p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); + } + + for (i = 0; i < BUF_CACHE_SIZE; i++) + buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); + + return FFS_SUCCESS; +} /* end of buf_init */ + +s32 buf_shutdown(struct super_block *sb) +{ + return FFS_SUCCESS; +} /* end of buf_shutdown */ + +/*======================================================================*/ +/* FAT Read/Write Functions */ +/*======================================================================*/ + +/* in : sb, loc + * out: content + * returns 0 on success + * -1 on error + */ +s32 FAT_read(struct super_block *sb, u32 loc, u32 *content) +{ + s32 ret; + + sm_P(&f_sem); + + ret = __FAT_read(sb, loc, content); + + sm_V(&f_sem); + + return ret; +} /* end of FAT_read */ + +s32 FAT_write(struct super_block *sb, u32 loc, u32 content) +{ + s32 ret; + + sm_P(&f_sem); + + ret = __FAT_write(sb, loc, content); + + sm_V(&f_sem); + + return ret; +} /* end of FAT_write */ + +static s32 __FAT_read(struct super_block *sb, u32 loc, u32 *content) +{ + s32 off; + u32 sec, _content; + u8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + if (off == (p_bd->sector_size-1)) { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + _content = (u32) fat_sector[off]; + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + _content |= (u32) fat_sector[0] << 8; + } else { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET16(fat_entry); + } + + if (loc & 1) + _content >>= 4; + + _content &= 0x00000FFF; + + if (_content >= CLUSTER_16(0x0FF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT16) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET16_A(fat_entry); + + _content &= 0x0000FFFF; + + if (_content >= CLUSTER_16(0xFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT32) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET32_A(fat_entry); + + _content &= 0x0FFFFFFF; + + if (_content >= CLUSTER_32(0x0FFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET32_A(fat_entry); + + if (_content >= CLUSTER_32(0xFFFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } + + *content = CLUSTER_32(~0); + return 0; +} /* end of __FAT_read */ + +static s32 __FAT_write(struct super_block *sb, u32 loc, u32 content) +{ + s32 off; + u32 sec; + u8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + + content &= 0x00000FFF; + + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + if (loc & 1) { /* odd */ + + content <<= 4; + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F)); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + fat_sector[0] = (u8)(content >> 8); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0x000F; + + SET16(fat_entry, content); + } + } else { /* even */ + fat_sector[off] = (u8)(content); + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (u8)(content); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8)); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0xF000; + + SET16(fat_entry, content); + } + } + } + + else if (p_fs->vol_type == FAT16) { + + content &= 0x0000FFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET16_A(fat_entry, content); + } + + else if (p_fs->vol_type == FAT32) { + + content &= 0x0FFFFFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + content |= GET32_A(fat_entry) & 0xF0000000; + + SET32_A(fat_entry, content); + } + + else { /* p_fs->vol_type == EXFAT */ + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET32_A(fat_entry, content); + } + + FAT_modify(sb, sec); + return 0; +} /* end of __FAT_write */ + +u8 *FAT_getblk(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return bp->buf_bh->b_data; + } + + bp = FAT_cache_get(sb, sec); + + FAT_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + FAT_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + FAT_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->FAT_cache_lru_list); + return NULL; + } + + return bp->buf_bh->b_data; +} /* end of FAT_getblk */ + +void FAT_modify(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) + sector_write(sb, sec, bp->buf_bh, 0); +} /* end of FAT_modify */ + +void FAT_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_release_all */ + +void FAT_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_sync */ + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, u32 sec) +{ + s32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + + WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " + "It will make system panic.\n"); + + touch_buffer(bp->buf_bh); + return bp; + } + } + return NULL; +} /* end of FAT_cache_find */ + +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->FAT_cache_lru_list.prev; + + + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return bp; +} /* end of FAT_cache_get */ + +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + s32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of FAT_cache_insert_hash */ + +static void FAT_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of FAT_cache_remove_hash */ + +/*======================================================================*/ +/* Buffer Read/Write Functions */ +/*======================================================================*/ + +u8 *buf_getblk(struct super_block *sb, u32 sec) +{ + u8 *buf; + + sm_P(&b_sem); + + buf = __buf_getblk(sb, sec); + + sm_V(&b_sem); + + return buf; +} /* end of buf_getblk */ + +static u8 *__buf_getblk(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = buf_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return bp->buf_bh->b_data; + } + + bp = buf_cache_get(sb, sec); + + buf_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + buf_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + buf_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + return NULL; + } + + return bp->buf_bh->b_data; + +} /* end of __buf_getblk */ + +void buf_modify(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + sector_write(sb, sec, bp->buf_bh, 0); + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_modify */ + +void buf_lock(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + bp->flag |= LOCKBIT; + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_lock */ + +void buf_unlock(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) + bp->flag &= ~(LOCKBIT); + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_unlock */ + +void buf_release(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + } + + sm_V(&b_sem); +} /* end of buf_release */ + +void buf_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if (bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_release_all */ + +void buf_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_sync */ + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, u32 sec) +{ + s32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->buf_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + touch_buffer(bp->buf_bh); + return bp; + } + } + return NULL; +} /* end of buf_cache_find */ + +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, u32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->buf_cache_lru_list.prev; + while (bp->flag & LOCKBIT) + bp = bp->prev; + + + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return bp; +} /* end of buf_cache_get */ + +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + s32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); + + hp = &(p_fs->buf_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of buf_cache_insert_hash */ + +static void buf_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of buf_cache_remove_hash */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->next = list->next; + bp->prev = list; + list->next->prev = bp; + list->next = bp; +} /* end of buf_cache_push_to_mru */ + +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev = list->prev; + bp->next = list; + list->prev->next = bp; + list->prev = bp; +} /* end of buf_cache_push_to_lru */ + +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_mru(bp, list); +} /* end of buf_cache_move_to_mru */ + +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_lru(bp, list); +} /* end of buf_cache_move_to_lru */ diff --git a/fs/exfat/exfat_cache.h b/fs/exfat/exfat_cache.h new file mode 100755 index 000000000000..d82aad58fa64 --- /dev/null +++ b/fs/exfat/exfat_cache.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.h */ +/* PURPOSE : Header File for exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CACHE_H +#define _EXFAT_CACHE_H + +#include +#include +#include "exfat_config.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define LOCKBIT 0x01 +#define DIRTYBIT 0x02 + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct __BUF_CACHE_T { + struct __BUF_CACHE_T *next; + struct __BUF_CACHE_T *prev; + struct __BUF_CACHE_T *hash_next; + struct __BUF_CACHE_T *hash_prev; + s32 drv; + u32 sec; + u32 flag; + struct buffer_head *buf_bh; +} BUF_CACHE_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 buf_init(struct super_block *sb); +s32 buf_shutdown(struct super_block *sb); +s32 FAT_read(struct super_block *sb, u32 loc, u32 *content); +s32 FAT_write(struct super_block *sb, u32 loc, u32 content); +u8 *FAT_getblk(struct super_block *sb, u32 sec); +void FAT_modify(struct super_block *sb, u32 sec); +void FAT_release_all(struct super_block *sb); +void FAT_sync(struct super_block *sb); +u8 *buf_getblk(struct super_block *sb, u32 sec); +void buf_modify(struct super_block *sb, u32 sec); +void buf_lock(struct super_block *sb, u32 sec); +void buf_unlock(struct super_block *sb, u32 sec); +void buf_release(struct super_block *sb, u32 sec); +void buf_release_all(struct super_block *sb); +void buf_sync(struct super_block *sb); + +#endif /* _EXFAT_CACHE_H */ diff --git a/fs/exfat/exfat_config.h b/fs/exfat/exfat_config.h new file mode 100755 index 000000000000..33c6525e449b --- /dev/null +++ b/fs/exfat/exfat_config.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_config.h */ +/* PURPOSE : Header File for exFAT Configuable Policies */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CONFIG_H +#define _EXFAT_CONFIG_H + +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Feature Config */ +/*----------------------------------------------------------------------*/ +#ifndef CONFIG_EXFAT_DISCARD +#define CONFIG_EXFAT_DISCARD 1 /* mount option -o discard support */ +#endif + +#ifndef CONFIG_EXFAT_DELAYED_SYNC +#define CONFIG_EXFAT_DELAYED_SYNC 0 +#endif + +#ifndef CONFIG_EXFAT_KERNEL_DEBUG +#define CONFIG_EXFAT_KERNEL_DEBUG 1 /* kernel debug features via ioctl */ +#endif + +#ifndef CONFIG_EXFAT_DEBUG_MSG +#define CONFIG_EXFAT_DEBUG_MSG 0 /* debugging message on/off */ +#endif + +#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE +#define CONFIG_EXFAT_DEFAULT_CODEPAGE 437 +#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" +#endif + +#endif /* _EXFAT_CONFIG_H */ diff --git a/fs/exfat/exfat_core.c b/fs/exfat/exfat_core.c new file mode 100755 index 000000000000..73c089f7f548 --- /dev/null +++ b/fs/exfat/exfat_core.c @@ -0,0 +1,5124 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_core.c */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "exfat_bitmap.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +#include + +static void __set_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 1; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 1; +#endif +} + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern u8 uni_upcase[]; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static u8 name_buf[MAX_PATH_LENGTH * MAX_CHARSET_SIZE]; + +static char *reserved_names[] = { + "AUX ", "CON ", "NUL ", "PRN ", + "COM1 ", "COM2 ", "COM3 ", "COM4 ", + "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", + "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", + NULL +}; + +static u8 free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 0 ~ 19 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, /* 20 ~ 39 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 40 ~ 59 */ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 60 ~ 79 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, /* 80 ~ 99 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, /* 100 ~ 119 */ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 120 ~ 139 */ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, /* 140 ~ 159 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 160 ~ 179 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, /* 180 ~ 199 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 200 ~ 219 */ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 220 ~ 239 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 240 ~ 254 */ +}; + +static u8 used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, /* 0 ~ 19 */ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, /* 20 ~ 39 */ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, /* 40 ~ 59 */ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 60 ~ 79 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, /* 80 ~ 99 */ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, /* 100 ~ 119 */ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, /* 120 ~ 139 */ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 140 ~ 159 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, /* 160 ~ 179 */ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, /* 180 ~ 199 */ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, /* 200 ~ 219 */ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 220 ~ 239 */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /* 240 ~ 255 */ +}; + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +/* ffsInit : roll back to the initial state of the file system */ +s32 ffsInit(void) +{ + s32 ret; + + ret = bdev_init(); + if (ret) + return ret; + + ret = fs_init(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsInit */ + +/* ffsShutdown : make free all memory-alloced global buffers */ +s32 ffsShutdown(void) +{ + s32 ret; + ret = fs_shutdown(); + if (ret) + return ret; + + ret = bdev_shutdown(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsShutdown */ + +/* ffsMountVol : mount the file system volume */ +s32 ffsMountVol(struct super_block *sb) +{ + int i, ret; + PBR_SECTOR_T *p_pbr; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + printk("[EXFAT] trying to mount...\n"); + + sm_init(&p_fs->v_sem); + p_fs->dev_ejected = FALSE; + + /* open the block device */ + if (bdev_open(sb)) + return FFS_MEDIAERR; + + if (p_bd->sector_size < sb->s_blocksize) + return FFS_MEDIAERR; + if (p_bd->sector_size > sb->s_blocksize) + sb_set_blocksize(sb, p_bd->sector_size); + + /* read Sector 0 */ + if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS) + return FFS_MEDIAERR; + + p_fs->PBR_sector = 0; + + p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data; + + /* check the validity of PBR */ + if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) { + brelse(tmp_bh); + bdev_close(sb); + return FFS_FORMATERR; + } + + /* fill fs_stuct */ + for (i = 0; i < 53; i++) + if (p_pbr->bpb[i]) + break; + + if (i < 53) { + if (GET16(p_pbr->bpb+11)) /* num_fat_sectors */ + ret = fat16_mount(sb, p_pbr); + else + ret = fat32_mount(sb, p_pbr); + } else { + ret = exfat_mount(sb, p_pbr); + } + + brelse(tmp_bh); + + if (ret) { + bdev_close(sb); + return ret; + } + + if (p_fs->vol_type == EXFAT) { + ret = load_alloc_bitmap(sb); + if (ret) { + bdev_close(sb); + return ret; + } + ret = load_upcase_table(sb); + if (ret) { + free_alloc_bitmap(sb); + bdev_close(sb); + return ret; + } + } + + if (p_fs->dev_ejected) { + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + bdev_close(sb); + return FFS_MEDIAERR; + } + + printk("[EXFAT] mounted successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsMountVol */ + +/* ffsUmountVol : umount the file system volume */ +s32 ffsUmountVol(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + printk("[EXFAT] trying to unmount...\n"); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + + FAT_release_all(sb); + buf_release_all(sb); + + /* close the block device */ + bdev_close(sb); + + if (p_fs->dev_ejected) { + printk("[EXFAT] unmounted with media errors. " + "device's already ejected.\n"); + return FFS_MEDIAERR; + } + + printk("[EXFAT] unmounted successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsUmountVol */ + +/* ffsGetVolInfo : get the information of a file system volume */ +s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->used_clusters == (u32) ~0) + p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); + + info->FatType = p_fs->vol_type; + info->ClusterSize = p_fs->cluster_size; + info->NumClusters = p_fs->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = p_fs->used_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsGetVolInfo */ + +/* ffsSyncVol : synchronize all file system volumes */ +s32 ffsSyncVol(struct super_block *sb, s32 do_sync) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* synchronize the file system */ + fs_sync(sb, do_sync); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSyncVol */ + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsLookupFile : lookup a file */ +s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + CHAIN_T dir; + UNI_NAME_T uni_name; + DOS_NAME_T dos_name; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + DPRINTK("ffsLookupFile entered\n"); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name); + if (ret) + return ret; + + /* search the file name for directories */ + dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL); + if (dentry < -1) + return FFS_NOTFOUND; + + fid->dir.dir = dir.dir; + fid->dir.size = dir.size; + fid->dir.flags = dir.flags; + fid->entry = dentry; + + if (dentry == -1) { + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x01; + fid->size = 0; + fid->start_clu = p_fs->root_dir; + } else { + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + fid->type = p_fs->fs_func->get_entry_type(ep); + fid->rwoffset = 0; + fid->hint_last_off = -1; + fid->attr = p_fs->fs_func->get_entry_attr(ep); + + fid->size = p_fs->fs_func->get_entry_size(ep2); + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } else { + fid->flags = p_fs->fs_func->get_entry_flag(ep2); + fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2); + } + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + DPRINTK("ffsLookupFile exited successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsLookupFile */ + +/* ffsCreateFile : create a file */ +s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* create a new file */ + ret = create_file(inode, &dir, &uni_name, mode, fid); + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateFile */ + +/* ffsReadFile : read data from a opened file */ +s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount) +{ + s32 offset, sec_offset, clu_offset; + u32 clu, LogSector; + u64 oneblkread, read_bytes; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count > (fid->size - fid->rwoffset)) + count = fid->size - fid->rwoffset; + + if (count == 0) { + if (rcount != NULL) + *rcount = 0; + return FFS_EOF; + } + + read_bytes = 0; + + while (count > 0) { + clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = fid->start_clu; + + if (fid->flags == 0x03) { + clu += clu_offset; + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkread = (u64)(p_bd->sector_size - offset); + if (oneblkread > count) + oneblkread = count; + + if ((offset == 0) && (oneblkread == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data), (s32) oneblkread); + } else { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) buffer)+read_bytes, ((char *) tmp_bh->b_data)+offset, (s32) oneblkread); + } + count -= oneblkread; + read_bytes += oneblkread; + fid->rwoffset += oneblkread; + } + brelse(tmp_bh); + +err_out: + /* set the size of read bytes */ + if (rcount != NULL) + *rcount = read_bytes; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadFile */ + +/* ffsWriteFile : write data into a opened file */ +s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount) +{ + s32 modified = FALSE, offset, sec_offset, clu_offset; + s32 num_clusters, num_alloc, num_alloced = (s32) ~0; + u32 clu, last_clu, LogSector, sector = 0; + u64 oneblkwrite, write_bytes; + CHAIN_T new_clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count == 0) { + if (wcount != NULL) + *wcount = 0; + return FFS_SUCCESS; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (fid->size == 0) + num_clusters = 0; + else + num_clusters = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + + write_bytes = 0; + + while (count > 0) { + clu_offset = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + clu = CLUSTER_32(~0); + else + clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu = clu; + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + if (clu == CLUSTER_32(~0)) { + num_alloc = (s32)((count-1) >> p_fs->cluster_size_bits) + 1; + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a chain of clusters */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); + if (num_alloced == 0) + break; + else if (num_alloced < 0) + return FFS_MEDIAERR; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + num_clusters += num_alloced; + clu = new_clu.dir; + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (s32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkwrite = (u64)(p_bd->sector_size - offset); + if (oneblkwrite > count) + oneblkwrite = count; + + if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + memcpy(((char *) tmp_bh->b_data), ((char *) buffer)+write_bytes, (s32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } else { + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + } else { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + } + + memcpy(((char *) tmp_bh->b_data)+offset, ((char *) buffer)+write_bytes, (s32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } + + count -= oneblkwrite; + write_bytes += oneblkwrite; + fid->rwoffset += oneblkwrite; + + fid->attr |= ATTR_ARCHIVE; + + if (fid->size < fid->rwoffset) { + fid->size = fid->rwoffset; + modified = TRUE; + } + } + + brelse(tmp_bh); + + /* (3) update the direcoty entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + goto err_out; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + goto err_out; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + + if (modified) { + if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) + p_fs->fs_func->set_entry_flag(ep2, fid->flags); + + if (p_fs->fs_func->get_entry_size(ep2) != fid->size) + p_fs->fs_func->set_entry_size(ep2, fid->size); + + if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + +err_out: + /* set the size of written bytes */ + if (wcount != NULL) + *wcount = write_bytes; + + if (num_alloced == 0) + return FFS_FULL; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsWriteFile */ + +/* ffsTruncateFile : resize the file length */ +s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size) +{ + s32 num_clusters; + u32 last_clu = CLUSTER_32(0), sector = 0; + CHAIN_T clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->size != old_size) { + printk(KERN_ERR "[EXFAT] truncate : can't skip it because of " + "size-mismatch(old:%lld->fid:%lld).\n" + ,old_size, fid->size); + } + + if (old_size <= new_size) + return FFS_SUCCESS; + + fs_set_vol_flags(sb, VOL_DIRTY); + + clu.dir = fid->start_clu; + clu.size = (s32)((old_size-1) >> p_fs->cluster_size_bits) + 1; + clu.flags = fid->flags; + + if (new_size > 0) { + num_clusters = (s32)((new_size-1) >> p_fs->cluster_size_bits) + 1; + + if (clu.flags == 0x03) { + clu.dir += num_clusters; + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + num_clusters--; + } + } + + clu.size -= num_clusters; + } + + fid->size = new_size; + fid->attr |= ATTR_ARCHIVE; + if (new_size == 0) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } + + /* (1) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + p_fs->fs_func->set_entry_size(ep2, new_size); + if (new_size == 0) { + p_fs->fs_func->set_entry_flag(ep2, 0x01); + p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); + } + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* (2) cut off from the FAT chain */ + if (last_clu != CLUSTER_32(0)) { + if (fid->flags == 0x01) + FAT_write(sb, last_clu, CLUSTER_32(~0)); + } + + /* (3) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu, 0); + + /* hint information */ + fid->hint_last_off = -1; + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsTruncateFile */ + +static void update_parent_info(FILE_ID_T *fid, struct inode *parent_inode) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); + FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); + + if (unlikely((parent_fid->flags != fid->dir.flags) + || (parent_fid->size != (fid->dir.size<cluster_size_bits)) + || (parent_fid->start_clu != fid->dir.dir))) { + + fid->dir.dir = parent_fid->start_clu; + fid->dir.flags = parent_fid->flags; + fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) + >> p_fs->cluster_size_bits); + } +} + +/* ffsMoveFile : move(rename) a old file into a new file */ +s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + s32 ret; + s32 dentry; + CHAIN_T olddir, newdir; + CHAIN_T *p_dir = NULL; + UNI_NAME_T uni_name; + DENTRY_T *ep; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + u8 *new_path = (u8 *) new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + FILE_ID_T *new_fid = NULL; + s32 new_entry = 0; + + /* check the validity of pointer parameters */ + if ((new_path == NULL) || (*new_path == '\0')) + return FFS_ERROR; + + update_parent_info(fid, old_parent_inode); + + olddir.dir = fid->dir.dir; + olddir.size = fid->dir.size; + olddir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the old file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + ep = get_entry_in_dir(sb, &olddir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + u32 entry_type; + + ret = FFS_MEDIAERR; + new_fid = &EXFAT_I(new_inode)->fid; + + update_parent_info(new_fid, new_parent_inode); + + p_dir = &(new_fid->dir); + new_entry = new_fid->entry; + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_DIR) { + CHAIN_T new_clu; + new_clu.dir = new_fid->start_clu; + new_clu.size = (s32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; + new_clu.flags = new_fid->flags; + + if (!is_dir_empty(sb, &new_clu)) + return FFS_FILEEXIST; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (olddir.dir == newdir.dir) + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); + else + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); + + if ((ret == FFS_SUCCESS) && new_inode) { + /* delete entries of new_dir */ + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) + goto out; + p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1); + } +out: +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsMoveFile */ + +/* ffsRemoveFile : remove a file */ +s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir, clu_to_free; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveFile */ + +/* ffsSetAttr : set the attribute of a given file */ +s32 ffsSetAttr(struct inode *inode, u32 attr) +{ + u32 type, sector = 0; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + ENTRY_SET_CACHE_T *es = NULL; + + if (fid->attr == attr) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + type = p_fs->fs_func->get_entry_type(ep); + + if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || + ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { + s32 err; + if (p_fs->dev_ejected) + err = FFS_MEDIAERR; + else + err = FFS_ERROR; + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + return err; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* set the file attribute */ + fid->attr = attr; + p_fs->fs_func->set_entry_attr(ep, attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetAttr */ + +/* ffsGetStat : get the information of a given file */ +s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + u32 sector = 0; + s32 count; + CHAIN_T dir; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es = NULL; + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + DPRINTK("ffsGetStat entered\n"); + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + info->Attr = ATTR_SUBDIR; + memset((char *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); + memset((char *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); + memset((char *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + strcpy(info->ShortName, "."); + strcpy(info->Name, "."); + + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + + if (p_fs->root_dir == CLUSTER_32(0)) /* FAT16 root_dir */ + info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS; + else + info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs = count; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + buf_lock(sb, sector); + } + + /* set FILE_INFO structure using the acquired DENTRY_T */ + info->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.MilliSecond = 0; + + memset((char *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + /* XXX this is very bad for exfat cuz name is already included in es. + API should be revised */ + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name); + if (*(uni_name.name) == 0x0 && p_fs->vol_type != EXFAT) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, info->Name, &uni_name); + + if (p_fs->vol_type == EXFAT) { + info->NumSubdirs = 2; + } else { + buf_unlock(sb, sector); + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, info->ShortName, &uni_name); + info->NumSubdirs = 0; + } + + info->Size = p_fs->fs_func->get_entry_size(ep2); + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + + if (is_dir) { + dir.dir = fid->start_clu; + dir.flags = 0x01; + + if (info->Size == 0) + info->Size = (u64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs += count; + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + DPRINTK("ffsGetStat exited successfully\n"); + return FFS_SUCCESS; +} /* end of ffsGetStat */ + +/* ffsSetStat : set the information of a given file */ +s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + u32 sector = 0; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + u8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + /* for other than exfat */ + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + + p_fs->fs_func->set_entry_attr(ep, info->Attr); + + /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.sec = info->CreateTimestamp.Second; + tm.min = info->CreateTimestamp.Minute; + tm.hour = info->CreateTimestamp.Hour; + tm.day = info->CreateTimestamp.Day; + tm.mon = info->CreateTimestamp.Month; + tm.year = info->CreateTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info->ModifyTimestamp.Second; + tm.min = info->ModifyTimestamp.Minute; + tm.hour = info->ModifyTimestamp.Hour; + tm.day = info->ModifyTimestamp.Day; + tm.mon = info->ModifyTimestamp.Month; + tm.year = info->ModifyTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY); + + + p_fs->fs_func->set_entry_size(ep2, info->Size); + + if (p_fs->vol_type != EXFAT) { + buf_modify(sb, sector); + } else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetStat */ + +s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu) +{ + s32 num_clusters, num_alloced, modified = FALSE; + u32 last_clu, sector = 0; + CHAIN_T new_clu; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + fid->rwoffset = (s64)(clu_offset) << p_fs->cluster_size_bits; + + if (EXFAT_I(inode)->mmu_private == 0) + num_clusters = 0; + else + num_clusters = (s32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; + + *clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = CLUSTER_32(~0); + else + *clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + *clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu = *clu; + if (FAT_read(sb, *clu, clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (*clu == CLUSTER_32(~0)) { + fs_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a cluster */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu); + if (num_alloced < 0) + return FFS_MEDIAERR; + else if (num_alloced == 0) + return FFS_FULL; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + num_clusters += num_alloced; + *clu = new_clu.dir; + + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + /* get stream entry */ + ep++; + } + + /* (3) update directory entry */ + if (modified) { + if (p_fs->vol_type != EXFAT) { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) + p_fs->fs_func->set_entry_flag(ep, fid->flags); + + if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* add number of new blocks to inode */ + inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); + } + + /* hint information */ + fid->hint_last_off = (s32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = *clu; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsCreateDir : create(make) a directory */ +s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid) +{ + s32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + DPRINTK("ffsCreateDir entered\n"); + + /* check the validity of directory name in the given old pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + ret = create_dir(inode, &dir, &uni_name, fid); + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateDir */ + +/* ffsReadDir : read a directory entry from the opened directory */ +s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + int i, dentry, clu_offset; + s32 dentries_per_clu, dentries_per_clu_bits = 0; + u32 type, sector; + CHAIN_T dir, clu; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_DIR) + return FFS_PERMISSIONERR; + + if (fid->entry == -1) { + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + } else { + dir.dir = fid->start_clu; + dir.size = (s32)(fid->size >> p_fs->cluster_size_bits); + dir.flags = fid->flags; + } + + dentry = (s32) fid->rwoffset; + + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + + if (dentry == dentries_per_clu) { + clu.dir = CLUSTER_32(~0); + } else { + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + } + } else { + dentries_per_clu = p_fs->dentries_per_clu; + dentries_per_clu_bits = ilog2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + + if (clu.flags == 0x03) { + clu.dir += clu_offset; + clu.size -= clu_offset; + } else { + /* hint_information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu.dir = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (dir.dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, §or); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + buf_lock(sb, sector); + dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + memset((char *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); + if (*(uni_name.name) == 0x0 && p_fs->vol_type != EXFAT) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); + buf_unlock(sb, sector); + + if (p_fs->vol_type == EXFAT) { + ep = get_entry_in_dir(sb, &clu, i+1, NULL); + if (!ep) + return FFS_MEDIAERR; + } else { + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); + } + + dir_entry->Size = p_fs->fs_func->get_entry_size(ep); + + /* hint information */ + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + } else { + fid->hint_last_off = dentry >> dentries_per_clu_bits; + fid->hint_last_clu = clu.dir; + } + + fid->rwoffset = (s64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; + } + + if (dir.dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + } + } + + *(dir_entry->Name) = '\0'; + + fid->rwoffset = (s64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadDir */ + +/* ffsRemoveDir : remove a directory */ +s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + s32 dentry; + CHAIN_T dir, clu_to_free; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((dir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (s32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + if (!is_dir_empty(sb, &clu_to_free)) + return FFS_FILEEXIST; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 1); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; + +#ifdef CONFIG_EXFAT_DELAYED_SYNC + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveDir */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +/* + * File System Management Functions + */ + +s32 fs_init(void) +{ + /* critical check for system requirement on size of DENTRY_T structure */ + if (sizeof(DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) + return FFS_ALIGNMENTERR; + + return FFS_SUCCESS; +} /* end of fs_init */ + +s32 fs_shutdown(void) +{ + return FFS_SUCCESS; +} /* end of fs_shutdown */ + +void fs_set_vol_flags(struct super_block *sb, u32 new_flag) +{ + PBR_SECTOR_T *p_pbr; + BPBEX_T *p_bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_flag == new_flag) + return; + + p_fs->vol_flag = new_flag; + + if (p_fs->vol_type == EXFAT) { + if (p_fs->pbr_bh == NULL) { + if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) + return; + } + + p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; + p_bpb = (BPBEX_T *) p_pbr->bpb; + SET16(p_bpb->vol_flags, (u16) new_flag); + + /* XXX duyoung + what can we do here? (cuz fs_set_vol_flags() is void) */ + if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh))) + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1); + else + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0); + } +} /* end of fs_set_vol_flags */ + +void fs_sync(struct super_block *sb, s32 do_sync) +{ + if (do_sync) + bdev_sync(sb); +} /* end of fs_sync */ + +void fs_error(struct super_block *sb) +{ + struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; + + if (opts->errors == EXFAT_ERRORS_PANIC) + panic("[EXFAT] Filesystem panic from previous error\n"); + else if ((opts->errors == EXFAT_ERRORS_RO) && !(sb->s_flags & MS_RDONLY)) { + sb->s_flags |= MS_RDONLY; + printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n"); + } +} + +/* + * Cluster Management Functions + */ + +s32 clear_cluster(struct super_block *sb, u32 clu) +{ + u32 s, n; + s32 ret = FFS_SUCCESS; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (clu == CLUSTER_32(0)) { /* FAT16 root_dir */ + s = p_fs->root_start_sector; + n = p_fs->data_start_sector; + } else { + s = START_SECTOR(clu); + n = s + p_fs->sectors_per_clu; + } + + for (; s < n; s++) { + ret = sector_read(sb, s, &tmp_bh, 0); + if (ret != FFS_SUCCESS) + return ret; + + memset((char *) tmp_bh->b_data, 0x0, p_bd->sector_size); + ret = sector_write(sb, s, tmp_bh, 0); + if (ret != FFS_SUCCESS) + break; + } + + brelse(tmp_bh); + return ret; +} /* end of clear_cluster */ + +s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain) +{ + int i, num_clusters = 0; + u32 new_clu, last_clu = CLUSTER_32(~0), read_clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + new_clu = p_chain->dir; + if (new_clu == CLUSTER_32(~0)) + new_clu = p_fs->clu_srch_ptr; + else if (new_clu >= p_fs->num_clusters) + new_clu = 2; + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, new_clu, &read_clu) != 0) + return -1; + + if (read_clu == CLUSTER_32(0)) { + if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0) + return -1; + num_clusters++; + + if (p_chain->dir == CLUSTER_32(~0)) + p_chain->dir = new_clu; + else { + if (FAT_write(sb, last_clu, new_clu) < 0) + return -1; + } + + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + return num_clusters; + } + } + if ((++new_clu) >= p_fs->num_clusters) + new_clu = 2; + } + + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + return num_clusters; +} /* end of fat_alloc_cluster */ + +s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain) +{ + s32 num_clusters = 0; + u32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + hint_clu = p_chain->dir; + if (hint_clu == CLUSTER_32(~0)) { + hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2); + if (hint_clu == CLUSTER_32(~0)) + return 0; + } else if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + p_chain->flags = 0x01; + } + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) { + if (new_clu != hint_clu) { + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + + if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS) + return -1; + + num_clusters++; + + if (p_chain->flags == 0x01) { + if (FAT_write(sb, new_clu, CLUSTER_32(~0)) < 0) + return -1; + } + + if (p_chain->dir == CLUSTER_32(~0)) { + p_chain->dir = new_clu; + } else { + if (p_chain->flags == 0x01) { + if (FAT_write(sb, last_clu, new_clu) < 0) + return -1; + } + } + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return num_clusters; + } + + hint_clu = new_clu + 1; + if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + } + + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return num_clusters; +} /* end of exfat_alloc_cluster */ + +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 num_clusters = 0; + u32 clu, prev; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + int i; + u32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->size <= 0) + return; + + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + prev = clu; + if (FAT_read(sb, clu, &clu) == -1) + break; + + if (FAT_write(sb, prev, CLUSTER_32(0)) < 0) + break; + num_clusters++; + + } while (clu != CLUSTER_32(~0)); + + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of fat_free_cluster */ + +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse) +{ + s32 num_clusters = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + int i; + u32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + + if (p_chain->size <= 0) { + printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, " + "because of zero-size truncation\n" + ,p_chain->dir); + return; + } + + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) + buf_release(sb, sector+i); + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + + if (FAT_read(sb, clu, &clu) == -1) + break; + num_clusters++; + } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); + } + + if (p_fs->used_clusters != (u32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of exfat_free_cluster */ + +u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) +{ + u32 clu, next; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + clu += p_chain->size - 1; + } else { + while ((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { + if (p_fs->dev_ejected) + break; + clu = next; + } + } + + return clu; +} /* end of find_last_cluster */ + +s32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) +{ + int i, count = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return 0; + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + count = p_chain->size; + } else { + for (i = 2; i < p_fs->num_clusters; i++) { + count++; + if (FAT_read(sb, clu, &clu) != 0) + return 0; + if (clu == CLUSTER_32(~0)) + break; + } + } + + return count; +} /* end of count_num_clusters */ + +s32 fat_count_used_clusters(struct super_block *sb) +{ + int i, count = 0; + u32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, i, &clu) != 0) + break; + if (clu != CLUSTER_32(0)) + count++; + } + + return count; +} /* end of fat_count_used_clusters */ + +s32 exfat_count_used_clusters(struct super_block *sb) +{ + int i, map_i, map_b, count = 0; + u8 k; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + map_i = map_b = 0; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + count += used_bit[k]; + + if ((++map_b) >= p_bd->sector_size) { + map_i++; + map_b = 0; + } + } + + return count; +} /* end of exfat_count_used_clusters */ + +void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len) +{ + if (len == 0) + return; + + while (len > 1) { + if (FAT_write(sb, chain, chain+1) < 0) + break; + chain++; + len--; + } + FAT_write(sb, chain, CLUSTER_32(~0)); +} /* end of exfat_chain_cont_cluster */ + +/* + * Allocation Bitmap Management Functions + */ + +s32 load_alloc_bitmap(struct super_block *sb) +{ + int i, j, ret; + u32 map_size; + u32 type, sector; + CHAIN_T clu; + BMAP_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + + if (ep->flags == 0x0) { + p_fs->map_clu = GET32_A(ep->start_clu); + map_size = (u32) GET64_A(ep->size); + + p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; + + p_fs->vol_amap = (struct buffer_head **) kmalloc(sizeof(struct buffer_head *) * p_fs->map_sectors, GFP_KERNEL); + if (p_fs->vol_amap == NULL) + return FFS_MEMORYERR; + + sector = START_SECTOR(p_fs->map_clu); + + for (j = 0; j < p_fs->map_sectors; j++) { + p_fs->vol_amap[j] = NULL; + ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1); + if (ret != FFS_SUCCESS) { + /* release all buffers and free vol_amap */ + i = 0; + while (i < j) + brelse(p_fs->vol_amap[i++]); + + if (p_fs->vol_amap) + kfree(p_fs->vol_amap); + p_fs->vol_amap = NULL; + return ret; + } + } + + p_fs->pbr_bh = NULL; + return FFS_SUCCESS; + } + } + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + return FFS_FORMATERR; +} /* end of load_alloc_bitmap */ + +void free_alloc_bitmap(struct super_block *sb) +{ + int i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + brelse(p_fs->pbr_bh); + + for (i = 0; i < p_fs->map_sectors; i++) + __brelse(p_fs->vol_amap[i]); + + if (p_fs->vol_amap) + kfree(p_fs->vol_amap); + p_fs->vol_amap = NULL; +} /* end of free_alloc_bitmap */ + +s32 set_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, b; + u32 sector; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + exfat_bitmap_set((u8 *) p_fs->vol_amap[i]->b_data, b); + + return sector_write(sb, sector, p_fs->vol_amap[i], 0); +} /* end of set_alloc_bitmap */ + +s32 clr_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, b; + u32 sector; +#ifdef CONFIG_EXFAT_DISCARD + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + int ret; +#endif /* CONFIG_EXFAT_DISCARD */ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + exfat_bitmap_clear((u8 *) p_fs->vol_amap[i]->b_data, b); + + return sector_write(sb, sector, p_fs->vol_amap[i], 0); + +#ifdef CONFIG_EXFAT_DISCARD + if (opts->discard) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); +#else + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); +#endif + if (ret == -EOPNOTSUPP) { + printk(KERN_WARNING "discard not supported by device, disabling"); + opts->discard = 0; + } + } +#endif /* CONFIG_EXFAT_DISCARD */ +} /* end of clr_alloc_bitmap */ + +u32 test_alloc_bitmap(struct super_block *sb, u32 clu) +{ + int i, map_i, map_b; + u32 clu_base, clu_free; + u8 k, clu_mask; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (p_bd->sector_size_bits + 3); + map_b = (clu >> 3) & p_bd->sector_size_mask; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((u8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < p_fs->num_clusters) + return clu_free; + } + clu_base += 8; + + if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) { + if ((++map_i) >= p_fs->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return CLUSTER_32(~0); +} /* end of test_alloc_bitmap */ + +void sync_alloc_bitmap(struct super_block *sb) +{ + int i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_amap == NULL) + return; + + for (i = 0; i < p_fs->map_sectors; i++) + sync_dirty_buffer(p_fs->vol_amap[i]); +} /* end of sync_alloc_bitmap */ + +/* + * Upcase table Management Functions + */ +s32 __load_upcase_table(struct super_block *sb, u32 sector, u32 num_sectors, u32 utbl_checksum) +{ + int i, ret = FFS_ERROR; + u32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + struct buffer_head *tmp_bh = NULL; + + u8 skip = FALSE; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + u32 checksum = 0; + + upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL); + if (upcase_table == NULL) + return FFS_MEMORYERR; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + num_sectors += sector; + + while (sector < num_sectors) { + ret = sector_read(sb, sector, &tmp_bh, 1); + if (ret != FFS_SUCCESS) { + DPRINTK("sector read (0x%X)fail\n", sector); + goto error; + } + sector++; + + for (i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { + uni = GET16(((u8 *) tmp_bh->b_data)+i); + + checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+i); + checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1) + *(((u8 *) tmp_bh->b_data)+(i+1)); + + if (skip) { + DPRINTK("skip from 0x%X ", index); + index += uni; + DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if (uni == index) + index++; + else if (uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (upcase_table[col_index] == NULL) { + DPRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL); + if (upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + } + if (index >= 0xFFFF && utbl_checksum == checksum) { + if (tmp_bh) + brelse(tmp_bh); + return FFS_SUCCESS; + } + ret = FFS_ERROR; +error: + if (tmp_bh) + brelse(tmp_bh); + free_upcase_table(sb); + return ret; +} + +s32 __load_default_upcase_table(struct super_block *sb) +{ + int i, ret = FFS_ERROR; + u32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + u8 skip = FALSE; + u32 index = 0; + u16 uni = 0; + u16 **upcase_table; + + upcase_table = p_fs->vol_utbl = (u16 **) kmalloc(UTBL_COL_COUNT * sizeof(u16 *), GFP_KERNEL); + if (upcase_table == NULL) + return FFS_MEMORYERR; + memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *)); + + for (i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { + uni = GET16(uni_upcase + i); + if (skip) { + DPRINTK("skip from 0x%X ", index); + index += uni; + DPRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if (uni == index) + index++; + else if (uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + u16 col_index = get_col_index(index); + + if (upcase_table[col_index] == NULL) { + DPRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (u16 *) kmalloc(UTBL_ROW_COUNT * sizeof(u16), GFP_KERNEL); + if (upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for (j = 0; j < UTBL_ROW_COUNT; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + + if (index >= 0xFFFF) + return FFS_SUCCESS; + +error: + /* FATAL error: default upcase table has error */ + free_upcase_table(sb); + return ret; +} + +s32 load_upcase_table(struct super_block *sb) +{ + int i; + u32 tbl_clu, tbl_size; + u32 type, sector, num_sectors; + CHAIN_T clu; + CASE_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + while (clu.dir != CLUSTER_32(~0)) { + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_UPCASE) + continue; + + tbl_clu = GET32_A(ep->start_clu); + tbl_size = (u32) GET64_A(ep->size); + + sector = START_SECTOR(tbl_clu); + num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1; + if (__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS) + break; + else + return FFS_SUCCESS; + } + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + /* load default upcase table */ + return __load_default_upcase_table(sb); +} /* end of load_upcase_table */ + +void free_upcase_table(struct super_block *sb) +{ + u32 i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + u16 **upcase_table; + + upcase_table = p_fs->vol_utbl; + for (i = 0; i < UTBL_COL_COUNT; i++) { + if (upcase_table[i]) + kfree(upcase_table[i]); + } + + if (p_fs->vol_utbl) + kfree(p_fs->vol_utbl); + p_fs->vol_utbl = NULL; +} /* end of free_upcase_table */ + +/* + * Directory Entry Management Functions + */ + +u32 fat_get_entry_type(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (*(ep->name) == 0x0) + return TYPE_UNUSED; + + else if (*(ep->name) == 0xE5) + return TYPE_DELETED; + + else if (ep->attr == ATTR_EXTEND) + return TYPE_EXTEND; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) + return TYPE_VOLUME; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) + return TYPE_DIR; + + return TYPE_FILE; +} /* end of fat_get_entry_type */ + +u32 exfat_get_entry_type(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (ep->type == 0x0) { + return TYPE_UNUSED; + } else if (ep->type < 0x80) { + return TYPE_DELETED; + } else if (ep->type == 0x80) { + return TYPE_INVALID; + } else if (ep->type < 0xA0) { + if (ep->type == 0x81) { + return TYPE_BITMAP; + } else if (ep->type == 0x82) { + return TYPE_UPCASE; + } else if (ep->type == 0x83) { + return TYPE_VOLUME; + } else if (ep->type == 0x85) { + if (GET16_A(ep->attr) & ATTR_SUBDIR) + return TYPE_DIR; + else + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } else if (ep->type < 0xC0) { + if (ep->type == 0xA0) + return TYPE_GUID; + else if (ep->type == 0xA1) + return TYPE_PADDING; + else if (ep->type == 0xA2) + return TYPE_ACLTAB; + return TYPE_BENIGN_PRI; + } else if (ep->type < 0xE0) { + if (ep->type == 0xC0) + return TYPE_STREAM; + else if (ep->type == 0xC1) + return TYPE_EXTEND; + else if (ep->type == 0xC2) + return TYPE_ACL; + return TYPE_CRITICAL_SEC; + } + + return TYPE_BENIGN_SEC; +} /* end of exfat_get_entry_type */ + +void fat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) + *(ep->name) = 0x0; + + else if (type == TYPE_DELETED) + *(ep->name) = 0xE5; + + else if (type == TYPE_EXTEND) + ep->attr = ATTR_EXTEND; + + else if (type == TYPE_DIR) + ep->attr = ATTR_SUBDIR; + + else if (type == TYPE_FILE) + ep->attr = ATTR_ARCHIVE; + + else if (type == TYPE_SYMLINK) + ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; +} /* end of fat_set_entry_type */ + +void exfat_set_entry_type(DENTRY_T *p_entry, u32 type) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) { + ep->type = 0x0; + } else if (type == TYPE_DELETED) { + ep->type &= ~0x80; + } else if (type == TYPE_STREAM) { + ep->type = 0xC0; + } else if (type == TYPE_EXTEND) { + ep->type = 0xC1; + } else if (type == TYPE_BITMAP) { + ep->type = 0x81; + } else if (type == TYPE_UPCASE) { + ep->type = 0x82; + } else if (type == TYPE_VOLUME) { + ep->type = 0x83; + } else if (type == TYPE_DIR) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_SUBDIR); + } else if (type == TYPE_FILE) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE); + } else if (type == TYPE_SYMLINK) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK); + } +} /* end of exfat_set_entry_type */ + +u32 fat_get_entry_attr(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return (u32) ep->attr; +} /* end of fat_get_entry_attr */ + +u32 exfat_get_entry_attr(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + return (u32) GET16_A(ep->attr); +} /* end of exfat_get_entry_attr */ + +void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + ep->attr = (u8) attr; +} /* end of fat_set_entry_attr */ + +void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + SET16_A(ep->attr, (u16) attr); +} /* end of exfat_set_entry_attr */ + +u8 fat_get_entry_flag(DENTRY_T *p_entry) +{ + return 0x01; +} /* end of fat_get_entry_flag */ + +u8 exfat_get_entry_flag(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return ep->flags; +} /* end of exfat_get_entry_flag */ + +void fat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ +} /* end of fat_set_entry_flag */ + +void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flags) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + ep->flags = flags; +} /* end of exfat_set_entry_flag */ + +u32 fat_get_entry_clu0(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return ((u32) GET16_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo); +} /* end of fat_get_entry_clu0 */ + +u32 exfat_get_entry_clu0(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return GET32_A(ep->start_clu); +} /* end of exfat_get_entry_clu0 */ + +void fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); +} /* end of fat_set_entry_clu0 */ + +void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET32_A(ep->start_clu, start_clu); +} /* end of exfat_set_entry_clu0 */ + +u64 fat_get_entry_size(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return (u64) GET32_A(ep->size); +} /* end of fat_get_entry_size */ + +u64 exfat_get_entry_size(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return GET64_A(ep->valid_size); +} /* end of exfat_get_entry_size */ + +void fat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET32_A(ep->size, (u32) size); +} /* end of fat_set_entry_size */ + +void exfat_set_entry_size(DENTRY_T *p_entry, u64 size) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of exfat_set_entry_size */ + +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of fat_get_entry_time */ + +void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t = 0x00, d = 0x21; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + case TM_ACCESS: + t = GET16_A(ep->access_time); + d = GET16_A(ep->access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of exfat_get_entry_time */ + +void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + } +} /* end of fat_set_entry_time */ + +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode) +{ + u16 t, d; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + case TM_ACCESS: + SET16_A(ep->access_time, t); + SET16_A(ep->access_date, d); + break; + } +} /* end of exfat_set_entry_time */ + +s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size) +{ + u32 sector; + DOS_DENTRY_T *dos_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + init_dos_entry(dos_ep, type, start_clu); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of fat_init_dir_entry */ + +s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size) +{ + u32 sector; + u8 flags; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* we cannot use get_entry_set_in_dir here because file ep is not initialized yet */ + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + init_file_entry(file_ep, type); + buf_modify(sb, sector); + + init_strm_entry(strm_ep, flags, start_clu, size); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of exfat_init_dir_entry */ + +s32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i; + u32 sector; + u8 chksum; + u16 *uniname = p_uniname->name; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + dos_ep->lcase = p_dosname->name_case; + memcpy(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); + buf_modify(sb, sector); + + if ((--num_entries) > 0) { + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (i = 1; i < num_entries; i++) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i, chksum, uniname); + buf_modify(sb, sector); + uniname += 13; + } + + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i+0x40, chksum, uniname); + buf_modify(sb, sector); + } + + return FFS_SUCCESS; +} /* end of fat_init_ext_entry */ + +s32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i; + u32 sector; + u16 *uniname = p_uniname->name; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + file_ep->num_ext = (u8)(num_entries - 1); + buf_modify(sb, sector); + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + strm_ep->name_len = p_uniname->name_len; + SET16_A(strm_ep->name_hash, p_uniname->name_hash); + buf_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return FFS_MEDIAERR; + + init_name_entry(name_ep, uniname); + buf_modify(sb, sector); + uniname += 15; + } + + update_dir_checksum(sb, p_dir, entry); + + return FFS_SUCCESS; +} /* end of exfat_init_ext_entry */ + +void init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 start_clu) +{ + TIMESTAMP_T tm, *tp; + + fat_set_entry_type((DENTRY_T *) ep, type); + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); + SET32_A(ep->size, 0); + + tp = tm_current(&tm); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + SET16_A(ep->access_date, 0); + ep->create_time_ms = 0; +} /* end of init_dos_entry */ + +void init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname) +{ + int i; + u8 end = FALSE; + + fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->order = (u8) order; + ep->sysid = 0; + ep->checksum = chksum; + SET16_A(ep->start_clu, 0); + + for (i = 0; i < 10; i += 2) { + if (!end) { + SET16(ep->unicode_0_4+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16(ep->unicode_0_4+i, 0xFFFF); + } + } + + for (i = 0; i < 12; i += 2) { + if (!end) { + SET16_A(ep->unicode_5_10+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_5_10+i, 0xFFFF); + } + } + + for (i = 0; i < 4; i += 2) { + if (!end) { + SET16_A(ep->unicode_11_12+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_11_12+i, 0xFFFF); + } + } +} /* end of init_ext_entry */ + +void init_file_entry(FILE_DENTRY_T *ep, u32 type) +{ + TIMESTAMP_T tm, *tp; + + exfat_set_entry_type((DENTRY_T *) ep, type); + + tp = tm_current(&tm); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); + ep->create_time_ms = 0; + ep->modify_time_ms = 0; + ep->access_time_ms = 0; +} /* end of init_file_entry */ + +void init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size) +{ + exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); + ep->flags = flags; + SET32_A(ep->start_clu, start_clu); + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of init_strm_entry */ + +void init_name_entry(NAME_DENTRY_T *ep, u16 *uniname) +{ + int i; + + exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->flags = 0x0; + + for (i = 0; i < 30; i++, i++) { + SET16_A(ep->unicode_0_14+i, *uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} /* end of init_name_entry */ + +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + int i; + u32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = num_entries-1; i >= order; i--) { + ep = get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of fat_delete_dir_entry */ + +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries) +{ + int i; + u32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = order; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of exfat_delete_dir_entry */ + +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry) +{ + int i, num_entries; + u32 sector; + u16 chksum; + FILE_DENTRY_T *file_ep; + DENTRY_T *ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return; + + buf_lock(sb, sector); + + num_entries = (s32) file_ep->num_ext + 1; + chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, NULL); + if (!ep) { + buf_unlock(sb, sector); + return; + } + + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); + } + + SET16_A(file_ep->checksum, chksum); + buf_modify(sb, sector); + buf_unlock(sb, sector); +} /* end of update_dir_checksum */ + +void update_dir_checksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + DENTRY_T *ep; + u16 chksum = 0; + s32 chksum_type = CS_DIR_ENTRY, i; + + ep = (DENTRY_T *)&(es->__buf); + for (i = 0; i < es->num_entries; i++) { + DPRINTK("update_dir_checksum_with_entry_set ep %p\n", ep); + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); + ep++; + chksum_type = CS_DEFAULT; + } + + ep = (DENTRY_T *)&(es->__buf); + SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); + write_whole_entry_set(sb, es); +} + +static s32 _walk_fat_chain(struct super_block *sb, CHAIN_T *p_dir, s32 byte_offset, u32 *clu) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + s32 clu_offset; + u32 cur_clu; + + clu_offset = byte_offset >> p_fs->cluster_size_bits; + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (FAT_read(sb, cur_clu, &cur_clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (clu) + *clu = cur_clu; + return FFS_SUCCESS; +} +s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector, s32 *offset) +{ + s32 off, ret; + u32 clu = 0; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + off = entry << DENTRY_SIZE_BITS; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + *offset = off & p_bd->sector_size_mask; + *sector = off >> p_bd->sector_size_bits; + *sector += p_fs->root_start_sector; + } else { + ret = _walk_fat_chain(sb, p_dir, off, &clu); + if (ret != FFS_SUCCESS) + return ret; + + off &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + *offset = off & p_bd->sector_size_mask; /* byte offset in sector */ + *sector = off >> p_bd->sector_size_bits; /* sector offset in cluster */ + *sector += START_SECTOR(clu); + } + return FFS_SUCCESS; +} /* end of find_location */ + +DENTRY_T *get_entry_with_sector(struct super_block *sb, u32 sector, s32 offset) +{ + u8 *buf; + + buf = buf_getblk(sb, sector); + + if (buf == NULL) + return NULL; + + return (DENTRY_T *)(buf + offset); +} /* end of get_entry_with_sector */ + +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector) +{ + s32 off; + u32 sec; + u8 *buf; + + if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) + return NULL; + + buf = buf_getblk(sb, sec); + + if (buf == NULL) + return NULL; + + if (sector != NULL) + *sector = sec; + return (DENTRY_T *)(buf + off); +} /* end of get_entry_in_dir */ + + +/* returns a set of dentries for a file or dir. + * Note that this is a copy (dump) of dentries so that user should call write_entry_set() + * to apply changes made in this entry set to the real device. + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ + +#define ES_MODE_STARTED 0 +#define ES_MODE_GET_FILE_ENTRY 1 +#define ES_MODE_GET_STRM_ENTRY 2 +#define ES_MODE_GET_NAME_ENTRY 3 +#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 +ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep) +{ + s32 off, ret, byte_offset; + u32 clu = 0; + u32 sec, entry_type; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + ENTRY_SET_CACHE_T *es = NULL; + DENTRY_T *ep, *pos; + u8 *buf; + u8 num_entries; + s32 mode = ES_MODE_STARTED; + + DPRINTK("get_entry_set_in_dir entered\n"); + DPRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); + + byte_offset = entry << DENTRY_SIZE_BITS; + ret = _walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return NULL; + + + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + + + ep = (DENTRY_T *)(buf + off); + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type != TYPE_FILE) + && (entry_type != TYPE_DIR)) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; + else + num_entries = type; + + DPRINTK("trying to kmalloc %zx bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); + es = kmalloc(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), GFP_KERNEL); + if (es == NULL) + goto err_out; + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = (DENTRY_T *) &(es->__buf); + + while(num_entries) { + /* instead of copying whole sector, we will check every entry. + * this will provide minimum stablity and consistancy. + */ + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) + goto err_out; + + switch (mode) { + case ES_MODE_STARTED: + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) + mode = ES_MODE_GET_FILE_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_FILE_ENTRY: + if (entry_type == TYPE_STREAM) + mode = ES_MODE_GET_STRM_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_STRM_ENTRY: + if (entry_type == TYPE_EXTEND) + mode = ES_MODE_GET_NAME_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_NAME_ENTRY: + if (entry_type == TYPE_EXTEND) + break; + else if (entry_type == TYPE_STREAM) + goto err_out; + else if (entry_type & TYPE_CRITICAL_SEC) + mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) + goto err_out; + else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + goto err_out; + break; + } + + memcpy(pos, ep, sizeof(DENTRY_T)); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) { + /* get the next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + off = 0; + ep = (DENTRY_T *)(buf); + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = (DENTRY_T *)&(es->__buf); + + DPRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); + DPRINTK("get_entry_set_in_dir exited %p\n", es); + return es; +err_out: + DPRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); + if (es) + kfree(es); + return NULL; +} + +void release_entry_set(ENTRY_SET_CACHE_T *es) +{ + DPRINTK("release_entry_set %p\n", es); + if (es) + kfree(es); +} + + +static s32 __write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, u32 sec, s32 off, u32 count) +{ + s32 num_entries, buf_off = (off - es->offset); + u32 remaining_byte_in_sector, copy_entries; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + u32 clu; + u8 *buf, *esbuf = (u8 *)&(es->__buf); + + DPRINTK("__write_partial_entries_in_entry_set entered\n"); + DPRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); + num_entries = count; + + while (num_entries) { + /* white per sector base */ + remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off; + copy_entries = MIN(remaining_byte_in_sector >> DENTRY_SIZE_BITS , num_entries); + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + DPRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); + DPRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); + memcpy(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); + buf_modify(sb, sec); + num_entries -= copy_entries; + + if (num_entries) { + /* get next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + clu = GET_CLUSTER_FROM_SECTOR(sec); + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + off = 0; + buf_off += copy_entries << DENTRY_SIZE_BITS; + } + } + + DPRINTK("__write_partial_entries_in_entry_set exited successfully\n"); + return FFS_SUCCESS; +err_out: + DPRINTK("__write_partial_entries_in_entry_set failed\n"); + return FFS_ERROR; +} + +/* write back all entries in entry set */ +s32 write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + return __write_partial_entries_in_entry_set(sb, es, es->sector, es->offset, es->num_entries); +} + +/* write back some entries in entry set */ +s32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count) +{ + s32 ret, byte_offset, off; + u32 clu=0, sec; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + CHAIN_T dir; + + /* vaidity check */ + if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries) + return FFS_ERROR; + + dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector); + dir.flags = es->alloc_flag; + dir.size = 0xffffffff; /* XXX */ + + byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits; + byte_offset += ((void **)ep - &(es->__buf)) + es->offset; + + ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return ret; + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + return __write_partial_entries_in_entry_set(sb, es, sec, off, count); +} + +/* search EMPTY CONTINUOUS "num_entries" entries */ +s32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries) +{ + int i, dentry, num_empty = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + if (p_fs->hint_uentry.dir == p_dir->dir) { + if (p_fs->hint_uentry.entry == -1) + return -1; + + clu.dir = p_fs->hint_uentry.clu.dir; + clu.size = p_fs->hint_uentry.clu.size; + clu.flags = p_fs->hint_uentry.clu.flags; + + dentry = p_fs->hint_uentry.entry; + } else { + p_fs->hint_uentry.entry = -1; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + dentry = 0; + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for (; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) { + num_empty++; + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = dentry; + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + } else if (type == TYPE_DELETED) { + num_empty++; + } else { + num_empty = 0; + } + + if (num_empty >= num_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + + if (p_fs->vol_type == EXFAT) + return dentry - (num_entries-1); + else + return dentry; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return -1; +} /* end of search_deleted_or_unused_entry */ + +s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries) +{ + s32 ret, dentry; + u32 last_clu, sector; + u64 size = 0; + CHAIN_T clu; + DENTRY_T *ep = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + return search_deleted_or_unused_entry(sb, p_dir, num_entries); + + while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) { + if (p_fs->dev_ejected) + break; + + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) + size = i_size_read(inode); + } + + last_clu = find_last_cluster(sb, p_dir); + clu.dir = last_clu + 1; + clu.size = 0; + clu.flags = p_dir->flags; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 1) + return -1; + + if (clear_cluster(sb, clu.dir) != FFS_SUCCESS) + return -1; + + /* (2) append to the FAT chain */ + if (clu.flags != p_dir->flags) { + exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + p_fs->hint_uentry.clu.flags = 0x01; + } + if (clu.flags == 0x01) + if (FAT_write(sb, last_clu, clu.dir) < 0) + return -1; + + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = 0; + p_fs->hint_uentry.clu.flags = clu.flags; + } + p_fs->hint_uentry.clu.size++; + p_dir->size++; + + /* (3) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) { + size += p_fs->cluster_size; + + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, §or); + if (!ep) + return -1; + p_fs->fs_func->set_entry_size(ep, size); + p_fs->fs_func->set_entry_flag(ep, p_dir->flags); + buf_modify(sb, sector); + + update_dir_checksum(sb, &(fid->dir), fid->entry); + } + } + + i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); + EXFAT_I(inode)->mmu_private += p_fs->cluster_size; + EXFAT_I(inode)->fid.size += p_fs->cluster_size; + EXFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); + } + + return dentry; +} /* end of find_empty_entry */ + +/* return values of fat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +s32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type) +{ + int i, dentry = 0, lossy = FALSE, len; + s32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; + s32 dentries_per_clu; + u32 entry_type; + u16 entry_uniname[14], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + if ((type == TYPE_ALL) || (type == entry_type)) { + if (is_feasible_entry && has_ext_entry) + return dentry; + + dos_ep = (DOS_DENTRY_T *) ep; + if ((!lossy) && (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name))) + return dentry; + } + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + ext_ep = (EXT_DENTRY_T *) ep; + if (ext_ep->order > 0x40) { + order = (s32)(ext_ep->order - 0x40); + uniname = p_uniname->name + 13 * (order-1); + } else { + order = (s32) ext_ep->order; + uniname -= 13; + } + + len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) + is_feasible_entry = FALSE; + + *(uniname+len) = unichar; + } + has_ext_entry = TRUE; + } else if (entry_type == TYPE_UNUSED) { + return -2; + } else { + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + + return -2; +} /* end of fat_find_dir_entry */ + +/* return values of exfat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +s32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type) +{ + int i = 0, dentry = 0, num_ext_entries = 0, len, step; + s32 order = 0, is_feasible_entry = FALSE; + s32 dentries_per_clu, num_empty = 0; + u32 entry_type; + u16 entry_uniname[16], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (u16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = -1; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + while (i < dentries_per_clu) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + step = 1; + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + is_feasible_entry = FALSE; + + if (p_fs->hint_uentry.entry == -1) { + num_empty++; + + if (num_empty == 1) { + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) + p_fs->hint_uentry.entry = dentry - (num_empty-1); + } + + if (entry_type == TYPE_UNUSED) + return -2; + } else { + num_empty = 0; + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + file_ep = (FILE_DENTRY_T *) ep; + if ((type == TYPE_ALL) || (type == entry_type)) { + num_ext_entries = file_ep->num_ext; + is_feasible_entry = TRUE; + } else { + is_feasible_entry = FALSE; + step = file_ep->num_ext + 1; + } + } else if (entry_type == TYPE_STREAM) { + if (is_feasible_entry) { + strm_ep = (STRM_DENTRY_T *) ep; + if (p_uniname->name_hash == GET16_A(strm_ep->name_hash) && + p_uniname->name_len == strm_ep->name_len) { + order = 1; + } else { + is_feasible_entry = FALSE; + step = num_ext_entries; + } + } + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + name_ep = (NAME_DENTRY_T *) ep; + + if ((++order) == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) { + is_feasible_entry = FALSE; + step = num_ext_entries - order + 1; + } else if (order == num_ext_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + return dentry - (num_ext_entries); + } + + *(uniname+len) = unichar; + } + } else { + is_feasible_entry = FALSE; + } + } + + i += step; + dentry += step; + } + + i -= dentries_per_clu; + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + } + + return -2; +} /* end of exfat_find_dir_entry */ + +/* returns -1 on error */ +s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + s32 count = 0; + u8 chksum; + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (entry--; entry >= 0; entry--) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && + (ext_ep->checksum == chksum)) { + count++; + if (ext_ep->order > 0x40) + return count; + } else { + return count; + } + } + + return count; +} /* end of fat_count_ext_entries */ + +/* returns -1 on error */ +s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry) +{ + int i, count = 0; + u32 type; + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; + DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { + ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ext_ep); + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) + count++; + else + return count; + } + + return count; +} /* end of exfat_count_ext_entries */ + +/* returns -1 on error */ +s32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type) +{ + int i, count = 0; + s32 dentries_per_clu; + u32 entry_type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_UNUSED) + return count; + if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) + continue; + + if ((type == TYPE_ALL) || (type == entry_type)) + count++; + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return count; +} /* end of count_dos_name_entries */ + +bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir) +{ + int i, count = 0; + s32 dentries_per_clu; + u32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + break; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + return TRUE; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + return FALSE; + } else { + if (p_fs->vol_type == EXFAT) + return FALSE; + if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2)) + return FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + break; + } + } + + return TRUE; +} /* end of is_dir_empty */ + +/* + * Name Conversion Functions + */ + +/* input : dir, uni_name + output : num_of_entry, dos_name(format : aaaaaa~1.bbb) */ +s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *entries, DOS_NAME_T *p_dosname) +{ + s32 ret, num_entries, lossy = FALSE; + char **r; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + num_entries = p_fs->fs_func->calc_num_entries(p_uniname); + if (num_entries == 0) + return FFS_INVALIDPATH; + + if (p_fs->vol_type != EXFAT) { + nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); + + if (lossy) { + ret = fat_generate_dos_name(sb, p_dir, p_dosname); + if (ret) + return ret; + } else { + for (r = reserved_names; *r; r++) { + if (!strncmp((void *) p_dosname->name, *r, 8)) + return FFS_INVALIDPATH; + } + + if (p_dosname->name_case != 0xFF) + num_entries = 1; + } + + if (num_entries > 1) + p_dosname->name_case = 0x0; + } + + *entries = num_entries; + + return FFS_SUCCESS; +} /* end of get_num_entries_and_dos_name */ + +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode) +{ + DOS_NAME_T dos_name; + + if (mode == 0x0) + dos_name.name_case = 0x0; + else + dos_name.name_case = ep->lcase; + + memcpy(dos_name.name, ep->name, DOS_NAME_LENGTH); + nls_dosname_to_uniname(sb, p_uniname, &dos_name); +} /* end of get_uni_name_from_dos_entry */ + +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + int i; + EXT_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (entry--, i = 1; entry >= 0; entry--, i++) { + ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ep) + return; + + if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { + extract_uni_name_from_ext_entry(ep, uniname, i); + if (ep->order > 0x40) + return; + } else { + return; + } + + uniname += 13; + } +} /* end of fat_get_uni_name_from_ext_entry */ + +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname) +{ + int i; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (es == NULL || es->num_entries < 3) { + if (es) + release_entry_set(es); + return; + } + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) + extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); + else + goto out; + uniname += 15; + } + +out: + release_entry_set(es); +} /* end of exfat_get_uni_name_from_ext_entry */ + +s32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order) +{ + int i, len = 0; + + for (i = 0; i < 10; i += 2) { + *uniname = GET16(ep->unicode_0_4+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + if (order < 20) { + for (i = 0; i < 12; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + } else { + for (i = 0; i < 8; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + *uniname = 0x0; /* uniname[MAX_NAME_LENGTH-1] */ + return len; + } + + for (i = 0; i < 4; i += 2) { + *uniname = GET16_A(ep->unicode_11_12+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of extract_uni_name_from_ext_entry */ + +s32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order) +{ + int i, len = 0; + + for (i = 0; i < 30; i += 2) { + *uniname = GET16_A(ep->unicode_0_14+i); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} /* end of extract_uni_name_from_name_entry */ + +s32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) +{ + int i, j, count = 0, count_begin = FALSE; + s32 dentries_per_clu; + u32 type; + u8 bmap[128/* 1 ~ 1023 */]; + CHAIN_T clu; + DOS_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + memset(bmap, 0, sizeof bmap); + exfat_bitmap_set(bmap, 0); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + count = 0; + count_begin = FALSE; + + for (j = 0; j < 8; j++) { + if (ep->name[j] == ' ') + break; + + if (ep->name[j] == '~') { + count_begin = TRUE; + } else if (count_begin) { + if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) { + count = count * 10 + (ep->name[j] - '0'); + } else { + count = 0; + count_begin = FALSE; + } + } + } + + if ((count > 0) && (count < 1024)) + exfat_bitmap_set(bmap, count); + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + count = 0; + for (i = 0; i < 128; i++) { + if (bmap[i] != 0xFF) { + for (j = 0; j < 8; j++) { + if (exfat_bitmap_test(&(bmap[i]), j) == 0) { + count = (i << 3) + j; + break; + } + } + if (count != 0) + break; + } + } + + if ((count == 0) || (count >= 1024)) + return FFS_FILEEXIST; + else + fat_attach_count_to_dos_name(p_dosname->name, count); + + /* Now dos_name has DOS~????.EXT */ + return FFS_SUCCESS; +} /* end of generate_dos_name */ + +void fat_attach_count_to_dos_name(u8 *dosname, s32 count) +{ + int i, j, length; + char str_count[6]; + + snprintf(str_count, sizeof str_count, "~%d", count); + length = strlen(str_count); + + i = j = 0; + while (j <= (8 - length)) { + i = j; + if (dosname[j] == ' ') + break; + if (dosname[j] & 0x80) + j += 2; + else + j++; + } + + for (j = 0; j < length; i++, j++) + dosname[i] = (u8) str_count[j]; + + if (i == 7) + dosname[7] = ' '; + +} /* end of attach_count_to_dos_name */ + +s32 fat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 dos name entry + extended entries */ + return (len-1) / 13 + 2; + +} /* end of calc_num_enties */ + +s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + s32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return (len-1) / 15 + 3; + +} /* end of exfat_calc_num_enties */ + +u8 calc_checksum_1byte(void *data, s32 len, u8 chksum) +{ + int i; + u8 *c = (u8 *) data; + + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; + + return chksum; +} /* end of calc_checksum_1byte */ + +u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type) +{ + int i; + u8 *c = (u8 *) data; + + switch (type) { + case CS_DIR_ENTRY: + for (i = 0; i < len; i++, c++) { + if ((i == 2) || (i == 3)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + break; + default + : + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c; + } + + return chksum; +} /* end of calc_checksum_2byte */ + +u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type) +{ + int i; + u8 *c = (u8 *) data; + + switch (type) { + case CS_PBR_SECTOR: + for (i = 0; i < len; i++, c++) { + if ((i == 106) || (i == 107) || (i == 112)) + continue; + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (u32) *c; + } + break; + default + : + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (u32) *c; + } + + return chksum; +} /* end of calc_checksum_4byte */ + +/* + * Name Resolution Functions + */ + +/* return values of resolve_path() + > 0 : return the length of the path + < 0 : return error */ +s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) +{ + s32 lossy = FALSE; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (strlen(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return FFS_INVALIDPATH; + + strcpy(name_buf, path); + + nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); + if (lossy) + return FFS_INVALIDPATH; + + fid->size = i_size_read(inode); + + p_dir->dir = fid->start_clu; + p_dir->size = (s32)(fid->size >> p_fs->cluster_size_bits); + p_dir->flags = fid->flags; + + return FFS_SUCCESS; +} + +/* + * File Operation Functions + */ +static FS_FUNC_T fat_fs_func = { + .alloc_cluster = fat_alloc_cluster, + .free_cluster = fat_free_cluster, + .count_used_clusters = fat_count_used_clusters, + + .init_dir_entry = fat_init_dir_entry, + .init_ext_entry = fat_init_ext_entry, + .find_dir_entry = fat_find_dir_entry, + .delete_dir_entry = fat_delete_dir_entry, + .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry, + .count_ext_entries = fat_count_ext_entries, + .calc_num_entries = fat_calc_num_entries, + + .get_entry_type = fat_get_entry_type, + .set_entry_type = fat_set_entry_type, + .get_entry_attr = fat_get_entry_attr, + .set_entry_attr = fat_set_entry_attr, + .get_entry_flag = fat_get_entry_flag, + .set_entry_flag = fat_set_entry_flag, + .get_entry_clu0 = fat_get_entry_clu0, + .set_entry_clu0 = fat_set_entry_clu0, + .get_entry_size = fat_get_entry_size, + .set_entry_size = fat_set_entry_size, + .get_entry_time = fat_get_entry_time, + .set_entry_time = fat_set_entry_time, +}; + + +s32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + s32 num_reserved, num_root_sectors; + BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; + num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; + + p_fs->num_sectors = GET16(p_bpb->num_sectors); + if (p_fs->num_sectors == 0) + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + if (p_fs->num_clusters < FAT12_THRESHOLD) + p_fs->vol_type = FAT12; + else + p_fs->vol_type = FAT16; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = 0; + p_fs->dentries_in_root = GET16(p_bpb->num_root_entries); + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat16_mount */ + +s32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + s32 num_reserved; + BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = ilog2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + + p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = FAT32; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat32_mount */ + +static FS_FUNC_T exfat_fs_func = { + .alloc_cluster = exfat_alloc_cluster, + .free_cluster = exfat_free_cluster, + .count_used_clusters = exfat_count_used_clusters, + + .init_dir_entry = exfat_init_dir_entry, + .init_ext_entry = exfat_init_ext_entry, + .find_dir_entry = exfat_find_dir_entry, + .delete_dir_entry = exfat_delete_dir_entry, + .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry, + .count_ext_entries = exfat_count_ext_entries, + .calc_num_entries = exfat_calc_num_entries, + + .get_entry_type = exfat_get_entry_type, + .set_entry_type = exfat_set_entry_type, + .get_entry_attr = exfat_get_entry_attr, + .set_entry_attr = exfat_set_entry_attr, + .get_entry_flag = exfat_get_entry_flag, + .set_entry_flag = exfat_set_entry_flag, + .get_entry_clu0 = exfat_get_entry_clu0, + .set_entry_clu0 = exfat_set_entry_clu0, + .get_entry_size = exfat_get_entry_size, + .set_entry_size = exfat_set_entry_size, + .get_entry_time = exfat_get_entry_time, + .set_entry_time = exfat_set_entry_time, +}; + +s32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits; + p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits; + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->fat_length); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset); + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET64(p_bpb->vol_length); + p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = EXFAT; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = (u32) GET16(p_bpb->vol_flags); + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (u32) ~0; + + p_fs->fs_func = &exfat_fs_func; + + return FFS_SUCCESS; +} /* end of exfat_mount */ + +s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + u64 size; + CHAIN_T clu; + DOS_NAME_T dos_name, dot_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + clu.dir = CLUSTER_32(~0); + clu.size = 0; + clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 0) + return FFS_MEDIAERR; + else if (ret == 0) + return FFS_FULL; + + ret = clear_cluster(sb, clu.dir); + if (ret != FFS_SUCCESS) + return ret; + + if (p_fs->vol_type == EXFAT) { + size = p_fs->cluster_size; + } else { + size = 0; + + /* initialize the . and .. entry + Information for . points to itself + Information for .. points to parent dir */ + + dot_name.name_case = 0x0; + memcpy(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); + + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + + memcpy(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); + + if (p_dir->dir == p_fs->root_dir) + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0); + else + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); + + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + } + + /* (2) update the directory entry */ + /* make sub-dir entry in parent directory */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_SUBDIR; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = size; + fid->start_clu = clu.dir; + + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_dir */ + +s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid) +{ + s32 ret, dentry, num_entries; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster() */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + /* (1) update the directory entry */ + /* fill the dos name directory entry information of the created file. + the first cluster is not determined yet. (0) */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_ARCHIVE | mode; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + + fid->type = TYPE_FILE; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_file */ + +void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry) +{ + s32 num_entries; + u32 sector; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ep = get_entry_in_dir(sb, p_dir, entry, §or); + if (!ep) + return; + + buf_lock(sb, sector); + + /* buf_lock() before call count_ext_entries() */ + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep); + if (num_entries < 0) { + buf_unlock(sb, sector); + return; + } + num_entries++; + + buf_unlock(sb, sector); + + /* (1) update the directory entry */ + p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); +} /* end of remove_file */ + +s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry = -1, num_old_entries, num_new_entries; + u32 sector_old, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epold, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); + if (!epold) + return FFS_MEDIAERR; + + buf_lock(sb, sector_old); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_old); + return ret; + } + + if (num_old_entries < num_new_entries) { + newentry = find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_old); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_dir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + + if (p_fs->vol_type == EXFAT) { + epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); + buf_lock(sb, sector_old); + epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epold, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_old); + buf_unlock(sb, sector_old); + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return FFS_SUCCESS; +} /* end of rename_file */ + +s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + s32 ret, newentry, num_new_entries, num_old_entries; + u32 sector_mov, sector_new; + CHAIN_T clu; + DOS_NAME_T dos_name; + DENTRY_T *epmov, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); + if (!epmov) + return FFS_MEDIAERR; + + /* check if the source and target directory is the same */ + if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR && + p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir) + return FFS_INVALIDPATH; + + buf_lock(sb, sector_mov); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); + if (num_old_entries < 0) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_mov); + return ret; + } + + newentry = find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_mov); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_newdir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + + if (p_fs->vol_type == EXFAT) { + epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + buf_lock(sb, sector_mov); + epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + memcpy((void *) epnew, (void *) epmov, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { + /* change ".." pointer to new parent dir */ + clu.dir = p_fs->fs_func->get_entry_clu0(epnew); + clu.flags = 0x01; + + epnew = get_entry_in_dir(sb, &clu, 1, §or_new); + if (!epnew) + return FFS_MEDIAERR; + + if (p_newdir->dir == p_fs->root_dir) + p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); + else + p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); + buf_modify(sb, sector_new); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return FFS_SUCCESS; +} /* end of move_file */ + +/* + * Sector Read/Write Functions + */ + +s32 sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, 1, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_read */ + +s32 sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + + if (bh == NULL) { + printk("[EXFAT] sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, 1, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_write */ + +s32 multi_sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, num_secs, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_read */ + +s32 multi_sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync) +{ + s32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + printk("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + if (bh == NULL) { + printk("[EXFAT] multi_sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, num_secs, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_write */ diff --git a/fs/exfat/exfat_core.h b/fs/exfat/exfat_core.h new file mode 100755 index 000000000000..4bdfe4ecef3f --- /dev/null +++ b/fs/exfat/exfat_core.h @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_core.h */ +/* PURPOSE : Header File for exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_cache.h" + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + /* For Debugging Purpose */ + /* IOCTL code 'f' used by + * - file systems typically #0~0x1F + * - embedded terminal devices #128~ + * - exts for debugging purpose #99 + * number 100 and 101 is availble now but has possible conflicts + */ +#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) +#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) + +#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 +#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02 +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define DENTRY_SIZE 32 /* dir entry size */ +#define DENTRY_SIZE_BITS 5 + +/* PBR entries */ +#define PBR_SIGNATURE 0xAA55 +#define EXT_SIGNATURE 0xAA550000 +#define VOL_LABEL "NO NAME " /* size should be 11 */ +#define OEM_NAME "MSWIN4.1" /* size should be 8 */ +#define STR_FAT12 "FAT12 " /* size should be 8 */ +#define STR_FAT16 "FAT16 " /* size should be 8 */ +#define STR_FAT32 "FAT32 " /* size should be 8 */ +#define STR_EXFAT "EXFAT " /* size should be 8 */ +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +/* max number of clusters */ +#define FAT12_THRESHOLD 4087 /* 2^12 - 1 + 2 (clu 0 & 1) */ +#define FAT16_THRESHOLD 65527 /* 2^16 - 1 + 2 */ +#define FAT32_THRESHOLD 268435457 /* 2^28 - 1 + 2 */ +#define EXFAT_THRESHOLD 268435457 /* 2^28 - 1 + 2 */ + +/* file types */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_SYMLINK 0x015F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + +/* time modes */ +#define TM_CREATE 0 +#define TM_MODIFY 1 +#define TM_ACCESS 2 + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +#define CLUSTER_16(x) ((u16)(x)) +#define CLUSTER_32(x) ((u32)(x)) + +#define FALSE 0 +#define TRUE 1 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define START_SECTOR(x) \ + ((((x) - 2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector) + +#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ + ((((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) - 1)) == 0) + +#define GET_CLUSTER_FROM_SECTOR(sec) \ + ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) + 2) + +#define GET16(p_src) \ + (((u16)(p_src)[0]) | (((u16)(p_src)[1]) << 8)) +#define GET32(p_src) \ + (((u32)(p_src)[0]) | (((u32)(p_src)[1]) << 8) | \ + (((u32)(p_src)[2]) << 16) | (((u32)(p_src)[3]) << 24)) +#define GET64(p_src) \ + (((u64)(p_src)[0]) | (((u64)(p_src)[1]) << 8) | \ + (((u64)(p_src)[2]) << 16) | (((u64)(p_src)[3]) << 24) | \ + (((u64)(p_src)[4]) << 32) | (((u64)(p_src)[5]) << 40) | \ + (((u64)(p_src)[6]) << 48) | (((u64)(p_src)[7]) << 56)) + + +#define SET16(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u16)(src)) >> 8); \ + } while (0) +#define SET32(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u32)(src)) >> 8); \ + (p_dst)[2] = (u8)(((u32)(src)) >> 16); \ + (p_dst)[3] = (u8)(((u32)(src)) >> 24); \ + } while (0) +#define SET64(p_dst, src) \ + do { \ + (p_dst)[0] = (u8)(src); \ + (p_dst)[1] = (u8)(((u64)(src)) >> 8); \ + (p_dst)[2] = (u8)(((u64)(src)) >> 16); \ + (p_dst)[3] = (u8)(((u64)(src)) >> 24); \ + (p_dst)[4] = (u8)(((u64)(src)) >> 32); \ + (p_dst)[5] = (u8)(((u64)(src)) >> 40); \ + (p_dst)[6] = (u8)(((u64)(src)) >> 48); \ + (p_dst)[7] = (u8)(((u64)(src)) >> 56); \ + } while (0) + +#ifdef __LITTLE_ENDIAN +#define GET16_A(p_src) (*((u16 *)(p_src))) +#define GET32_A(p_src) (*((u32 *)(p_src))) +#define GET64_A(p_src) (*((u64 *)(p_src))) +#define SET16_A(p_dst, src) (*((u16 *)(p_dst)) = (u16)(src)) +#define SET32_A(p_dst, src) (*((u32 *)(p_dst)) = (u32)(src)) +#define SET64_A(p_dst, src) (*((u64 *)(p_dst)) = (u64)(src)) +#else /* BIG_ENDIAN */ +#define GET16_A(p_src) GET16(p_src) +#define GET32_A(p_src) GET32(p_src) +#define GET64_A(p_src) GET64(p_src) +#define SET16_A(p_dst, src) SET16(p_dst, src) +#define SET32_A(p_dst, src) SET32(p_dst, src) +#define SET64_A(p_dst, src) SET64(p_dst, src) +#endif + +/* Upcase tabel mecro */ +#define HIGH_INDEX_BIT (8) +#define HIGH_INDEX_MASK (0xFF00) +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) +#define UTBL_ROW_COUNT (1<> LOW_INDEX_BIT; +} +static inline u16 get_row_index(u16 i) +{ + return i & ~HIGH_INDEX_MASK; +} +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* MS_DOS FAT partition boot record (512 bytes) */ +typedef struct { + u8 jmp_boot[3]; + u8 oem_name[8]; + u8 bpb[109]; + u8 boot_code[390]; + u8 signature[2]; +} PBR_SECTOR_T; + +/* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */ +typedef struct { + u8 sector_size[2]; + u8 sectors_per_clu; + u8 num_reserved[2]; + u8 num_fats; + u8 num_root_entries[2]; + u8 num_sectors[2]; + u8 media_type; + u8 num_fat_sectors[2]; + u8 sectors_in_track[2]; + u8 num_heads[2]; + u8 num_hid_sectors[4]; + u8 num_huge_sectors[4]; + + u8 phy_drv_no; + u8 reserved; + u8 ext_signature; + u8 vol_serial[4]; + u8 vol_label[11]; + u8 vol_type[8]; +} BPB16_T; + +/* MS-DOS FAT32 BIOS parameter block (79 bytes) */ +typedef struct { + u8 sector_size[2]; + u8 sectors_per_clu; + u8 num_reserved[2]; + u8 num_fats; + u8 num_root_entries[2]; + u8 num_sectors[2]; + u8 media_type; + u8 num_fat_sectors[2]; + u8 sectors_in_track[2]; + u8 num_heads[2]; + u8 num_hid_sectors[4]; + u8 num_huge_sectors[4]; + u8 num_fat32_sectors[4]; + u8 ext_flags[2]; + u8 fs_version[2]; + u8 root_cluster[4]; + u8 fsinfo_sector[2]; + u8 backup_sector[2]; + u8 reserved[12]; + + u8 phy_drv_no; + u8 ext_reserved; + u8 ext_signature; + u8 vol_serial[4]; + u8 vol_label[11]; + u8 vol_type[8]; +} BPB32_T; + +/* MS-DOS EXFAT BIOS parameter block (109 bytes) */ +typedef struct { + u8 reserved1[53]; + u8 vol_offset[8]; + u8 vol_length[8]; + u8 fat_offset[4]; + u8 fat_length[4]; + u8 clu_offset[4]; + u8 clu_count[4]; + u8 root_cluster[4]; + u8 vol_serial[4]; + u8 fs_version[2]; + u8 vol_flags[2]; + u8 sector_size_bits; + u8 sectors_per_clu_bits; + u8 num_fats; + u8 phy_drv_no; + u8 perc_in_use; + u8 reserved2[7]; +} BPBEX_T; + +/* MS-DOS FAT file system information sector (512 bytes) */ +typedef struct { + u8 signature1[4]; + u8 reserved1[480]; + u8 signature2[4]; + u8 free_cluster[4]; + u8 next_cluster[4]; + u8 reserved2[14]; + u8 signature3[2]; +} FSI_SECTOR_T; + +/* MS-DOS FAT directory entry (32 bytes) */ +typedef struct { + u8 dummy[32]; +} DENTRY_T; + +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 attr; + u8 lcase; + u8 create_time_ms; + u8 create_time[2]; + u8 create_date[2]; + u8 access_date[2]; + u8 start_clu_hi[2]; + u8 modify_time[2]; + u8 modify_date[2]; + u8 start_clu_lo[2]; + u8 size[4]; +} DOS_DENTRY_T; + +/* MS-DOS FAT extended directory entry (32 bytes) */ +typedef struct { + u8 order; + u8 unicode_0_4[10]; + u8 attr; + u8 sysid; + u8 checksum; + u8 unicode_5_10[12]; + u8 start_clu[2]; + u8 unicode_11_12[4]; +} EXT_DENTRY_T; + +/* MS-DOS EXFAT file directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 num_ext; + u8 checksum[2]; + u8 attr[2]; + u8 reserved1[2]; + u8 create_time[2]; + u8 create_date[2]; + u8 modify_time[2]; + u8 modify_date[2]; + u8 access_time[2]; + u8 access_date[2]; + u8 create_time_ms; + u8 modify_time_ms; + u8 access_time_ms; + u8 reserved2[9]; +} FILE_DENTRY_T; + +/* MS-DOS EXFAT stream extension directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 reserved1; + u8 name_len; + u8 name_hash[2]; + u8 reserved2[2]; + u8 valid_size[8]; + u8 reserved3[4]; + u8 start_clu[4]; + u8 size[8]; +} STRM_DENTRY_T; + +/* MS-DOS EXFAT file name directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 unicode_0_14[30]; +} NAME_DENTRY_T; + +/* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 flags; + u8 reserved[18]; + u8 start_clu[4]; + u8 size[8]; +} BMAP_DENTRY_T; + +/* MS-DOS EXFAT up-case table directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 reserved1[3]; + u8 checksum[4]; + u8 reserved2[12]; + u8 start_clu[4]; + u8 size[8]; +} CASE_DENTRY_T; + +/* MS-DOS EXFAT volume label directory entry (32 bytes) */ +typedef struct { + u8 type; + u8 label_len; + u8 unicode_0_10[22]; + u8 reserved[8]; +} VOLM_DENTRY_T; + +/* unused entry hint information */ +typedef struct { + u32 dir; + s32 entry; + CHAIN_T clu; +} UENTRY_T; + +typedef struct { + s32 (*alloc_cluster)(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); + void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); + s32 (*count_used_clusters)(struct super_block *sb); + + s32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, + u32 start_clu, u64 size); + s32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); + s32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); + void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 offset, s32 num_entries); + void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); + s32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); + s32 (*calc_num_entries)(UNI_NAME_T *p_uniname); + + u32 (*get_entry_type)(DENTRY_T *p_entry); + void (*set_entry_type)(DENTRY_T *p_entry, u32 type); + u32 (*get_entry_attr)(DENTRY_T *p_entry); + void (*set_entry_attr)(DENTRY_T *p_entry, u32 attr); + u8 (*get_entry_flag)(DENTRY_T *p_entry); + void (*set_entry_flag)(DENTRY_T *p_entry, u8 flag); + u32 (*get_entry_clu0)(DENTRY_T *p_entry); + void (*set_entry_clu0)(DENTRY_T *p_entry, u32 clu0); + u64 (*get_entry_size)(DENTRY_T *p_entry); + void (*set_entry_size)(DENTRY_T *p_entry, u64 size); + void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); + void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +} FS_FUNC_T; + +typedef struct __FS_INFO_T { + u32 drv; /* drive ID */ + u32 vol_type; /* volume FAT type */ + u32 vol_id; /* volume serial number */ + + u32 num_sectors; /* num of sectors in volume */ + u32 num_clusters; /* num of clusters in volume */ + u32 cluster_size; /* cluster size in bytes */ + u32 cluster_size_bits; + u32 sectors_per_clu; /* cluster size in sectors */ + u32 sectors_per_clu_bits; + + u32 PBR_sector; /* PBR sector */ + u32 FAT1_start_sector; /* FAT1 start sector */ + u32 FAT2_start_sector; /* FAT2 start sector */ + u32 root_start_sector; /* root dir start sector */ + u32 data_start_sector; /* data area start sector */ + u32 num_FAT_sectors; /* num of FAT sectors */ + + u32 root_dir; /* root dir cluster */ + u32 dentries_in_root; /* num of dentries in root dir */ + u32 dentries_per_clu; /* num of dentries per cluster */ + + u32 vol_flag; /* volume dirty flag */ + struct buffer_head *pbr_bh; /* PBR sector */ + + u32 map_clu; /* allocation bitmap start cluster */ + u32 map_sectors; /* num of allocation bitmap sectors */ + struct buffer_head **vol_amap; /* allocation bitmap */ + + u16 **vol_utbl; /* upcase table */ + + u32 clu_srch_ptr; /* cluster search pointer */ + u32 used_clusters; /* number of used clusters */ + UENTRY_T hint_uentry; /* unused entry hint information */ + + u32 dev_ejected; /* block device operation error flag */ + + FS_FUNC_T *fs_func; + struct semaphore v_sem; + + /* FAT cache */ + BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; + BUF_CACHE_T FAT_cache_lru_list; + BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + + /* buf cache */ + BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; + BUF_CACHE_T buf_cache_lru_list; + BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; +} FS_INFO_T; + +#define ES_2_ENTRIES 2 +#define ES_3_ENTRIES 3 +#define ES_ALL_ENTRIES 0 + +typedef struct { + u32 sector; /* sector number that contains file_entry */ + s32 offset; /* byte offset in the sector */ + s32 alloc_flag; /* flag in stream entry. 01 for cluster chain, 03 for contig. clusteres. */ + u32 num_entries; + + /* __buf should be the last member */ + void *__buf; +} ENTRY_SET_CACHE_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* file system initialization & shutdown functions */ +s32 ffsInit(void); +s32 ffsShutdown(void); + +/* volume management functions */ +s32 ffsMountVol(struct super_block *sb); +s32 ffsUmountVol(struct super_block *sb); +s32 ffsCheckVol(struct super_block *sb); +s32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); +s32 ffsSyncVol(struct super_block *sb, s32 do_sync); + +/* file management functions */ +s32 ffsLookupFile(struct inode *inode, char *path, FILE_ID_T *fid); +s32 ffsCreateFile(struct inode *inode, char *path, u8 mode, FILE_ID_T *fid); +s32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount); +s32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount); +s32 ffsTruncateFile(struct inode *inode, u64 old_size, u64 new_size); +s32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); +s32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); +s32 ffsSetAttr(struct inode *inode, u32 attr); +s32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); +s32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); +s32 ffsMapCluster(struct inode *inode, s32 clu_offset, u32 *clu); + +/* directory management functions */ +s32 ffsCreateDir(struct inode *inode, char *path, FILE_ID_T *fid); +s32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); +s32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); + +/*----------------------------------------------------------------------*/ +/* External Function Declarations (NOT TO UPPER LAYER) */ +/*----------------------------------------------------------------------*/ + +/* fs management functions */ +s32 fs_init(void); +s32 fs_shutdown(void); +void fs_set_vol_flags(struct super_block *sb, u32 new_flag); +void fs_sync(struct super_block *sb, s32 do_sync); +void fs_error(struct super_block *sb); + +/* cluster management functions */ +s32 clear_cluster(struct super_block *sb, u32 clu); +s32 fat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); +s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc, CHAIN_T *p_chain); +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, s32 do_relse); +u32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); +s32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); +s32 fat_count_used_clusters(struct super_block *sb); +s32 exfat_count_used_clusters(struct super_block *sb); +void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len); + +/* allocation bitmap management functions */ +s32 load_alloc_bitmap(struct super_block *sb); +void free_alloc_bitmap(struct super_block *sb); +s32 set_alloc_bitmap(struct super_block *sb, u32 clu); +s32 clr_alloc_bitmap(struct super_block *sb, u32 clu); +u32 test_alloc_bitmap(struct super_block *sb, u32 clu); +void sync_alloc_bitmap(struct super_block *sb); + +/* upcase table management functions */ +s32 load_upcase_table(struct super_block *sb); +void free_upcase_table(struct super_block *sb); + +/* dir entry management functions */ +u32 fat_get_entry_type(DENTRY_T *p_entry); +u32 exfat_get_entry_type(DENTRY_T *p_entry); +void fat_set_entry_type(DENTRY_T *p_entry, u32 type); +void exfat_set_entry_type(DENTRY_T *p_entry, u32 type); +u32 fat_get_entry_attr(DENTRY_T *p_entry); +u32 exfat_get_entry_attr(DENTRY_T *p_entry); +void fat_set_entry_attr(DENTRY_T *p_entry, u32 attr); +void exfat_set_entry_attr(DENTRY_T *p_entry, u32 attr); +u8 fat_get_entry_flag(DENTRY_T *p_entry); +u8 exfat_get_entry_flag(DENTRY_T *p_entry); +void fat_set_entry_flag(DENTRY_T *p_entry, u8 flag); +void exfat_set_entry_flag(DENTRY_T *p_entry, u8 flag); +u32 fat_get_entry_clu0(DENTRY_T *p_entry); +u32 exfat_get_entry_clu0(DENTRY_T *p_entry); +void fat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu); +void exfat_set_entry_clu0(DENTRY_T *p_entry, u32 start_clu); +u64 fat_get_entry_size(DENTRY_T *p_entry); +u64 exfat_get_entry_size(DENTRY_T *p_entry); +void fat_set_entry_size(DENTRY_T *p_entry, u64 size); +void exfat_set_entry_size(DENTRY_T *p_entry, u64 size); +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, u8 mode); +s32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size); +s32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, u32 start_clu, u64 size); +s32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +s32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +void init_dos_entry(DOS_DENTRY_T *ep, u32 type, u32 start_clu); +void init_ext_entry(EXT_DENTRY_T *ep, s32 order, u8 chksum, u16 *uniname); +void init_file_entry(FILE_DENTRY_T *ep, u32 type); +void init_strm_entry(STRM_DENTRY_T *ep, u8 flags, u32 start_clu, u64 size); +void init_name_entry(NAME_DENTRY_T *ep, u16 *uniname); +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries); +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, s32 order, s32 num_entries); + +s32 find_location(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector, s32 *offset); +DENTRY_T *get_entry_with_sector(struct super_block *sb, u32 sector, s32 offset); +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 *sector); +ENTRY_SET_CACHE_T *get_entry_set_in_dir(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u32 type, DENTRY_T **file_ep); +void release_entry_set(ENTRY_SET_CACHE_T *es); +s32 write_whole_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +s32 write_partial_entries_in_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, u32 count); +s32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, s32 num_entries); +s32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, s32 num_entries); +s32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); +s32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 num_entries, DOS_NAME_T *p_dosname, u32 type); +s32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); +s32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, s32 entry, DENTRY_T *p_entry); +s32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, u32 type); +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, s32 entry); +void update_dir_checksum_with_entry_set(struct super_block *sb, ENTRY_SET_CACHE_T *es); +bool is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); + +/* name conversion functions */ +s32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, s32 *entries, DOS_NAME_T *p_dosname); +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, u8 mode); +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, s32 entry, u16 *uniname); +s32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, u16 *uniname, s32 order); +s32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, u16 *uniname, s32 order); +s32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); +void fat_attach_count_to_dos_name(u8 *dosname, s32 count); +s32 fat_calc_num_entries(UNI_NAME_T *p_uniname); +s32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); +u8 calc_checksum_1byte(void *data, s32 len, u8 chksum); +u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type); +u32 calc_checksum_4byte(void *data, s32 len, u32 chksum, s32 type); + +/* name resolution functions */ +s32 resolve_path(struct inode *inode, char *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); +s32 resolve_name(u8 *name, u8 **arg); + +/* file operation functions */ +s32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); +s32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +s32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, u8 mode, FILE_ID_T *fid); +void remove_file(struct inode *inode, CHAIN_T *p_dir, s32 entry); +s32 rename_file(struct inode *inode, CHAIN_T *p_dir, s32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); +s32 move_file(struct inode *inode, CHAIN_T *p_olddir, s32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); + +/* sector read/write functions */ +s32 sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 read); +s32 sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 sync); +s32 multi_sector_read(struct super_block *sb, u32 sec, struct buffer_head **bh, s32 num_secs, s32 read); +s32 multi_sector_write(struct super_block *sb, u32 sec, struct buffer_head *bh, s32 num_secs, s32 sync); + +#endif /* _EXFAT_H */ diff --git a/fs/exfat/exfat_data.c b/fs/exfat/exfat_data.c new file mode 100755 index 000000000000..65da07aff547 --- /dev/null +++ b/fs/exfat/exfat_data.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.c */ +/* PURPOSE : exFAT Configuable Data Definitions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_super.h" +#include "exfat_core.h" + +/*======================================================================*/ +/* */ +/* GLOBAL VARIABLE DEFINITIONS */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* File Manager */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Buffer Manager */ +/*----------------------------------------------------------------------*/ + +/* FAT cache */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +DECLARE_MUTEX(f_sem); +#else +DEFINE_SEMAPHORE(f_sem); +#endif +BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; +BUF_CACHE_T FAT_cache_lru_list; +BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + +/* buf cache */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +DECLARE_MUTEX(b_sem); +#else +DEFINE_SEMAPHORE(b_sem); +#endif +BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; +BUF_CACHE_T buf_cache_lru_list; +BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; diff --git a/fs/exfat/exfat_data.h b/fs/exfat/exfat_data.h new file mode 100755 index 000000000000..53b0e39397fa --- /dev/null +++ b/fs/exfat/exfat_data.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.h */ +/* PURPOSE : Header File for exFAT Configuable Constants */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_DATA_H +#define _EXFAT_DATA_H + +#include "exfat_config.h" + +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/* max number of root directory entries in FAT12/16 */ +/* (should be an exponential value of 2) */ +#define MAX_DENTRY 512 + +/* cache size (in number of sectors) */ +/* (should be an exponential value of 2) */ +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +#endif /* _EXFAT_DATA_H */ diff --git a/fs/exfat/exfat_nls.c b/fs/exfat/exfat_nls.c new file mode 100755 index 000000000000..a48b3d05a7c4 --- /dev/null +++ b/fs/exfat/exfat_nls.c @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.c */ +/* PURPOSE : exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_data.h" + +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat_core.h" + +#include + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static u16 bad_dos_chars[] = { + /* + , ; = [ ] */ + 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, + 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, + 0 +}; + +static u16 bad_uni_chars[] = { + /* " * / : < > ? \ | */ + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, + 0 +}; + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +static s32 convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy); +static s32 convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy); + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +u16 nls_upper(struct super_block *sb, u16 a) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (EXFAT_SB(sb)->options.casesensitive) + return a; + if (p_fs->vol_utbl != NULL && (p_fs->vol_utbl)[get_col_index(a)] != NULL) + return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; + else + return a; +} + +u16 *nls_wstrchr(u16 *str, u16 wchar) +{ + while (*str) { + if (*(str++) == wchar) + return str; + } + + return 0; +} + +s32 nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b) +{ + return strncmp((void *) a, (void *) b, DOS_NAME_LENGTH); +} /* end of nls_dosname_cmp */ + +s32 nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b) +{ + int i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (nls_upper(sb, *a) != nls_upper(sb, *b)) + return 1; + if (*a == 0x0) + return 0; + } + return 0; +} /* end of nls_uniname_cmp */ + +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *p_lossy) +{ + int i, j, len, lossy = FALSE; + u8 buf[MAX_CHARSET_SIZE]; + u8 lower = 0, upper = 0; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + u16 *p, *last_period; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + for (i = 0; i < DOS_NAME_LENGTH; i++) + *(dosname+i) = ' '; + + if (!nls_uniname_cmp(sb, uniname, (u16 *) UNI_CUR_DIR_NAME)) { + *(dosname) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) + *p_lossy = FALSE; + return; + } + + if (!nls_uniname_cmp(sb, uniname, (u16 *) UNI_PAR_DIR_NAME)) { + *(dosname) = '.'; + *(dosname+1) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) + *p_lossy = FALSE; + return; + } + + /* search for the last embedded period */ + last_period = NULL; + for (p = uniname; *p; p++) { + if (*p == (u16) '.') + last_period = p; + } + + i = 0; + while (i < DOS_NAME_LENGTH) { + if (i == 8) { + if (last_period == NULL) + break; + + if (uniname <= last_period) { + if (uniname < last_period) + lossy = TRUE; + uniname = last_period + 1; + } + } + + if (*uniname == (u16) '\0') { + break; + } else if (*uniname == (u16) ' ') { + lossy = TRUE; + } else if (*uniname == (u16) '.') { + if (uniname < last_period) + lossy = TRUE; + else + i = 8; + } else if (nls_wstrchr(bad_dos_chars, *uniname)) { + lossy = TRUE; + *(dosname+i) = '_'; + i++; + } else { + len = convert_uni_to_ch(nls, buf, *uniname, &lossy); + + if (len > 1) { + if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) + break; + + if ((i < 8) && ((i+len) > 8)) { + i = 8; + continue; + } + + lower = 0xFF; + + for (j = 0; j < len; j++, i++) + *(dosname+i) = *(buf+j); + } else { /* len == 1 */ + if ((*buf >= 'a') && (*buf <= 'z')) { + *(dosname+i) = *buf - ('a' - 'A'); + + if (i < 8) + lower |= 0x08; + else + lower |= 0x10; + } else if ((*buf >= 'A') && (*buf <= 'Z')) { + *(dosname+i) = *buf; + + if (i < 8) + upper |= 0x08; + else + upper |= 0x10; + } else { + *(dosname+i) = *buf; + } + i++; + } + } + + uniname++; + } + + if (*dosname == 0xE5) + *dosname = 0x05; + + if (*uniname != 0x0) + lossy = TRUE; + + if (upper & lower) + p_dosname->name_case = 0xFF; + else + p_dosname->name_case = lower; + + if (p_lossy != NULL) + *p_lossy = lossy; +} /* end of nls_uniname_to_dosname */ + +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + int i = 0, j, n = 0; + u8 buf[DOS_NAME_LENGTH+2]; + u8 *dosname = p_dosname->name; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + if (*dosname == 0x05) { + *buf = 0xE5; + i++; + n++; + } + + for (; i < 8; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + if (*(dosname+8) != ' ') { + *(buf+n) = '.'; + n++; + } + + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { + if (*(dosname+i) == ' ') + break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + *(buf+n) = '\0'; + + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(buf+i) == '\0') + break; + + i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); + + uniname++; + j++; + } + + *uniname = (u16) '\0'; +} /* end of nls_dosname_to_uniname */ + +void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring, UNI_NAME_T *p_uniname) +{ + int i, j, len; + u8 buf[MAX_CHARSET_SIZE]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + if (nls == NULL) { + len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, p_cstring, MAX_NAME_LENGTH); + p_cstring[len] = 0; + return; + } + + i = 0; + while (i < (MAX_NAME_LENGTH-1)) { + if (*uniname == (u16) '\0') + break; + + len = convert_uni_to_ch(nls, buf, *uniname, NULL); + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = (char) *(buf+j); + } else { /* len == 1 */ + *p_cstring++ = (char) *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; +} /* end of nls_uniname_to_cstring */ + +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy) +{ + int i, j, lossy = FALSE; + u8 *end_of_name; + u8 upname[MAX_NAME_LENGTH * 2]; + u16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + + /* strip all trailing spaces */ + end_of_name = p_cstring + strlen((char *) p_cstring); + + while (*(--end_of_name) == ' ') { + if (end_of_name < p_cstring) + break; + } + *(++end_of_name) = '\0'; + + if (strcmp((char *) p_cstring, ".") && strcmp((char *) p_cstring, "..")) { + + /* strip all trailing periods */ + while (*(--end_of_name) == '.') { + if (end_of_name < p_cstring) + break; + } + *(++end_of_name) = '\0'; + } + + if (*p_cstring == '\0') + lossy = TRUE; + + if (nls == NULL) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,101) + i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH, uniname); +#else + i = utf8s_to_utf16s(p_cstring, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, uniname, MAX_NAME_LENGTH); +#endif + for (j = 0; j < i; j++) + SET16_A(upname + j * 2, nls_upper(sb, uniname[j])); + uniname[i] = '\0'; + } + else { + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(p_cstring+i) == '\0') + break; + + i += convert_ch_to_uni(nls, uniname, (u8 *)(p_cstring+i), &lossy); + + if ((*uniname < 0x0020) || nls_wstrchr(bad_uni_chars, *uniname)) + lossy = TRUE; + + SET16_A(upname + j * 2, nls_upper(sb, *uniname)); + + uniname++; + j++; + } + + if (*(p_cstring+i) != '\0') + lossy = TRUE; + *uniname = (u16) '\0'; + } + + p_uniname->name_len = j; + p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); + + if (p_lossy != NULL) + *p_lossy = lossy; +} /* end of nls_cstring_to_uniname */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static s32 convert_ch_to_uni(struct nls_table *nls, u16 *uni, u8 *ch, s32 *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = (u16) ch[0]; + return 1; + } + + len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni); + if (len < 0) { + /* conversion failed */ + printk("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy = TRUE; + *uni = (u16) '_'; + if (!strcmp(nls->charset, "utf8")) + return 1; + else + return 2; + } + + return len; +} /* end of convert_ch_to_uni */ + +static s32 convert_uni_to_ch(struct nls_table *nls, u8 *ch, u16 uni, s32 *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = (u8) uni; + return 1; + } + + len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE); + if (len < 0) { + /* conversion failed */ + printk("%s: fail to use nls\n", __func__); + if (lossy != NULL) + *lossy = TRUE; + ch[0] = '_'; + return 1; + } + + return len; + +} /* end of convert_uni_to_ch */ diff --git a/fs/exfat/exfat_nls.h b/fs/exfat/exfat_nls.h new file mode 100755 index 000000000000..bc516d762e90 --- /dev/null +++ b/fs/exfat/exfat_nls.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.h */ +/* PURPOSE : Header File for exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_NLS_H +#define _EXFAT_NLS_H + +#include +#include + +#include "exfat_config.h" +#include "exfat_api.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define NUM_UPCASE 2918 + +#define DOS_CUR_DIR_NAME ". " +#define DOS_PAR_DIR_NAME ".. " + +#ifdef __LITTLE_ENDIAN +#define UNI_CUR_DIR_NAME ".\0" +#define UNI_PAR_DIR_NAME ".\0.\0" +#else +#define UNI_CUR_DIR_NAME "\0." +#define UNI_PAR_DIR_NAME "\0.\0." +#endif + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* DOS name stucture */ +typedef struct { + u8 name[DOS_NAME_LENGTH]; + u8 name_case; +} DOS_NAME_T; + +/* unicode name stucture */ +typedef struct { + u16 name[MAX_NAME_LENGTH]; + u16 name_hash; + u8 name_len; +} UNI_NAME_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* NLS management function */ +u16 nls_upper(struct super_block *sb, u16 a); +s32 nls_dosname_cmp(struct super_block *sb, u8 *a, u8 *b); +s32 nls_uniname_cmp(struct super_block *sb, u16 *a, u16 *b); +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, s32 *p_lossy); +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +void nls_uniname_to_cstring(struct super_block *sb, u8 *p_cstring, UNI_NAME_T *p_uniname); +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, u8 *p_cstring, s32 *p_lossy); + +#endif /* _EXFAT_NLS_H */ diff --git a/fs/exfat/exfat_oal.c b/fs/exfat/exfat_oal.c new file mode 100755 index 000000000000..9b3399826a5b --- /dev/null +++ b/fs/exfat/exfat_oal.c @@ -0,0 +1,190 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.c */ +/* PURPOSE : exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include + +#include "exfat_config.h" +#include "exfat_api.h" +#include "exfat_oal.h" + +/*======================================================================*/ +/* */ +/* SEMAPHORE FUNCTIONS */ +/* */ +/*======================================================================*/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +DECLARE_MUTEX(z_sem); +#else +DEFINE_SEMAPHORE(z_sem); +#endif + +s32 sm_init(struct semaphore *sm) +{ + sema_init(sm, 1); + return 0; +} /* end of sm_init */ + +s32 sm_P(struct semaphore *sm) +{ + down(sm); + return 0; +} /* end of sm_P */ + +void sm_V(struct semaphore *sm) +{ + up(sm); +} /* end of sm_V */ + + +/*======================================================================*/ +/* */ +/* REAL-TIME CLOCK FUNCTIONS */ +/* */ +/*======================================================================*/ + +extern struct timezone sys_tz; + +/* + * The epoch of FAT timestamp is 1980. + * : bits : value + * date: 0 - 4: day (1 - 31) + * date: 5 - 8: month (1 - 12) + * date: 9 - 15: year (0 - 127) from 1980 + * time: 0 - 4: sec (0 - 29) 2sec counts + * time: 5 - 10: min (0 - 59) + * time: 11 - 15: hour (0 - 23) + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) +{ + struct timespec ts = CURRENT_TIME_SEC; + time_t second = ts.tv_sec; + time_t day, leap_day, month, year; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->sec = 0; + tp->min = 0; + tp->hour = 0; + tp->day = 1; + tp->mon = 1; + tp->year = 0; + return tp; + } +#if BITS_PER_LONG == 64 + if (second >= UNIX_SECS_2108) { + tp->sec = 59; + tp->min = 59; + tp->hour = 23; + tp->day = 31; + tp->mon = 12; + tp->year = 127; + return tp; + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(leap_day, year); + if (year * 365 + leap_day > day) + year--; + + MAKE_LEAP_YEAR(leap_day, year); + + day -= year * 365 + leap_day; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->sec = second % SECS_PER_MIN; + tp->min = (second / SECS_PER_MIN) % 60; + tp->hour = (second / SECS_PER_HOUR) % 24; + tp->day = day + 1; + tp->mon = month; + tp->year = year; + + return tp; +} /* end of tm_current */ diff --git a/fs/exfat/exfat_oal.h b/fs/exfat/exfat_oal.h new file mode 100755 index 000000000000..b6dd7897ab6e --- /dev/null +++ b/fs/exfat/exfat_oal.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.h */ +/* PURPOSE : Header File for exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_OAL_H +#define _EXFAT_OAL_H + +#include +#include "exfat_config.h" +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions (Non-Configurable) */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +typedef struct { + u16 sec; /* 0 ~ 59 */ + u16 min; /* 0 ~ 59 */ + u16 hour; /* 0 ~ 23 */ + u16 day; /* 1 ~ 31 */ + u16 mon; /* 1 ~ 12 */ + u16 year; /* 0 ~ 127 (since 1980) */ +} TIMESTAMP_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +s32 sm_init(struct semaphore *sm); +s32 sm_P(struct semaphore *sm); +void sm_V(struct semaphore *sm); + +TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); + +#endif /* _EXFAT_OAL_H */ diff --git a/fs/exfat/exfat_super.c b/fs/exfat/exfat_super.c new file mode 100755 index 000000000000..13d8e46952b6 --- /dev/null +++ b/fs/exfat/exfat_super.c @@ -0,0 +1,2643 @@ +/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/file.c + * + * Written 1992,1993 by Werner Almesberger + * + * regular file handling primitives for fat-based filesystems + */ + +/* + * linux/fs/fat/inode.c + * + * Written 1992,1993 by Werner Almesberger + * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro + * + * Fixes: + * + * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 + */ + +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_core.h" + +#include "exfat_super.h" + +static struct kmem_cache *exfat_inode_cachep; + +static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE; +static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; + +extern struct timezone sys_tz; + +#define CHECK_ERR(x) BUG_ON(x) + +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size); + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t year = tp->Year; + time_t ld; + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) + ld++; + + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN + + tp->Hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY + + sys_tz.tz_minuteswest * SECS_PER_MIN; + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->Second = 0; + tp->Minute = 0; + tp->Hour = 0; + tp->Day = 1; + tp->Month = 1; + tp->Year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->Second = 59; + tp->Minute = 59; + tp->Hour = 23; + tp->Day = 31; + tp->Month = 12; + tp->Year = 127; + return; + } +#endif + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->Second = second % SECS_PER_MIN; + tp->Minute = (second / SECS_PER_MIN) % 60; + tp->Hour = (second / SECS_PER_HOUR) % 24; + tp->Day = day + 1; + tp->Month = month; + tp->Year = year; +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#else +static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int exfat_sync_inode(struct inode *inode); +static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); +static void exfat_detach(struct inode *inode); +static void exfat_attach(struct inode *inode, loff_t i_pos); +static inline unsigned long exfat_hash(loff_t i_pos); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) +static int exfat_write_inode(struct inode *inode, int wait); +#else +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +#endif +static void exfat_write_super(struct super_block *sb); + +static void __lock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + lock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_lock(&sbi->s_lock); +#endif +} + +static void __unlock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + unlock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_unlock(&sbi->s_lock); +#endif +} + +static int __is_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + return sb->s_dirt; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + return sbi->s_dirt; +#endif +} + +static void __set_sb_clean(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 0; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 0; +#endif +} + +static int __exfat_revalidate(struct dentry *dentry) +{ + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; +#endif + + if (dentry->d_inode) + return 1; + return __exfat_revalidate(dentry); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#else + unsigned int flags; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; +#endif + + flags = nd ? nd->flags : 0; +#endif + + if (dentry->d_inode) + return 1; + + if (!flags) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; +#else + if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) { + if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + } +#endif + + return __exfat_revalidate(dentry); +} + +static unsigned int __exfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int exfat_striptail_len(const struct qstr *qstr) +{ + return __exfat_striptail_len(qstr->len, qstr->name); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) +static int exfat_d_hash(struct dentry *dentry, struct qstr *qstr) +#else +static int exfat_d_hash(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + qstr->hash = full_name_hash(dentry, qstr->name, exfat_striptail_len(qstr)); +#else + qstr->hash = full_name_hash(qstr->name, exfat_striptail_len(qstr)); +#endif + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) +static int exfat_d_hashi(struct dentry *dentry, struct qstr *qstr) +#else +static int exfat_d_hashi(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +#endif +{ + struct super_block *sb = dentry->d_sb; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = exfat_striptail_len(qstr); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + hash = init_name_hash(dentry); +#else + hash = init_name_hash(); +#endif + while (len--) + hash = partial_name_hash(nls_upper(sb, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) +static int exfat_cmpi(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_cmpi(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) +static int exfat_cmpi(struct dentry *parent, struct qstr *a, struct qstr *b) +#else +static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; +#else + struct nls_table *t = EXFAT_SB(parent->d_sb)->nls_io; +#endif + unsigned int alen, blen; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + alen = exfat_striptail_len(a); + blen = exfat_striptail_len(b); +#else + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); +#endif + if (alen == blen) { + if (t == NULL) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + if (strncasecmp(a->name, b->name, alen) == 0) +#else + if (strncasecmp(name->name, str, alen) == 0) +#endif + return 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + } else if (nls_strnicmp(t, a->name, b->name, alen) == 0) +#else + } else if (nls_strnicmp(t, name->name, str, alen) == 0) +#endif + return 0; + } + return 1; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0) +static int exfat_cmp(const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_cmp(const struct dentry *parent, const struct dentry *dentry, + unsigned int len, const char *str, const struct qstr *name) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) +static int exfat_cmp(struct dentry *parent, struct qstr *a, + struct qstr *b) +#else +static int exfat_cmp(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +#endif +{ + unsigned int alen, blen; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + alen = exfat_striptail_len(a); + blen = exfat_striptail_len(b); +#else + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); +#endif + if (alen == blen) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + if (strncmp(a->name, b->name, alen) == 0) +#else + if (strncmp(name->name, str, alen) == 0) +#endif + return 0; + } + return 1; +} + +static const struct dentry_operations exfat_ci_dentry_ops = { + .d_revalidate = exfat_revalidate_ci, + .d_hash = exfat_d_hashi, + .d_compare = exfat_cmpi, +}; + +static const struct dentry_operations exfat_dentry_ops = { + .d_revalidate = exfat_revalidate, + .d_hash = exfat_d_hash, + .d_compare = exfat_cmp, +}; + +/*======================================================================*/ +/* Directory Entry Operations */ +/*======================================================================*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_readdir(struct file *filp, struct dir_context *ctx) +#else +static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) + struct inode *inode = file_inode(filp); +#else + struct inode *inode = filp->f_path.dentry->d_inode; +#endif + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + DIR_ENTRY_T de; + unsigned long inum; + loff_t cpos; + int err = 0; + + __lock_super(sb); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + cpos = ctx->pos; +#else + cpos = filp->f_pos; +#endif + /* Fake . and .. for the root directory. */ + if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { + while (cpos < 2) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit_dots(filp, ctx)) +#else + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) +#endif + goto out; + cpos++; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos++; +#else + filp->f_pos++; +#endif + } + if (cpos == 2) + cpos = 0; + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + +get_new: + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + err = FsReadDir(inode, &de); + if (err) { + /* at least we tried to read a sector + * move cpos to next sector position (should be aligned) + */ + if (err == FFS_MEDIAERR) { + cpos += 1 << p_bd->sector_size_bits; + cpos &= ~((1 << p_bd->sector_size_bits)-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!de.Name[0]) + goto end_of_dir; + + if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + + struct inode *tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit(ctx, de.Name, strlen(de.Name), inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) +#else + if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) +#endif + goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif + goto get_new; + +end_of_dir: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif +out: + __unlock_super(sb); + return err; +} + +static int exfat_ioctl_volume_id(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + + return p_fs->vol_id; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +static long exfat_generic_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +#endif +{ +#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) + #if !(LINUX_VERSION_CODE < KERNEL_VERSION(3,18,3)) + struct inode *inode = filp->f_path.dentry->d_inode; + #else + struct inode *inode = filp->f_dentry->d_inode; + #endif +#endif +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + unsigned int flags; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + + switch (cmd) { + case EXFAT_IOCTL_GET_VOLUME_ID: + return exfat_ioctl_volume_id(inode); +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + case EXFAT_IOC_GET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + flags = sbi->debug_flags; + return put_user(flags, (int __user *)arg); + } + case EXFAT_IOC_SET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (int __user *) arg)) + return -EFAULT; + + __lock_super(sb); + sbi->debug_flags = flags; + __unlock_super(sb); + + return 0; + } +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + default: + return -ENOTTY; /* Inappropriate ioctl for device */ + } +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +static int exfat_file_fsync(struct file *filp, struct dentry *dentry, + int datasync) +#else +static int exfat_file_fsync(struct file *filp, int datasync) +#endif +{ + struct inode *inode = filp->f_mapping->host; + struct super_block *sb = inode->i_sb; + int res, err; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) + res = simple_fsync(filp, dentry, datasync); +#else + res = generic_file_fsync(filp, datasync); +#endif + err = FsSyncVol(sb, 1); + + return res ? res : err; +} +#endif + +const struct file_operations exfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .iterate = exfat_readdir, +#else + .readdir = exfat_readdir, +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +#else +static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_create entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (u8 *) dentry->d_name.name, FM_REGULAR, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unnecessary. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_create exited\n"); + return err; +} + +static int exfat_find(struct inode *dir, struct qstr *qname, + FILE_ID_T *fid) +{ + int err; + + if (qname->len == 0) + return -ENOENT; + + err = FsLookupFile(dir, (u8 *) qname->name, fid); + if (err) + return -ENOENT; + + return 0; +} + +static int exfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +#else +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + int err; + FILE_ID_T fid; + loff_t i_pos; + u64 ret; + mode_t i_mode; + + __lock_super(sb); + DPRINTK("exfat_lookup entered\n"); + err = exfat_find(dir, &dentry->d_name, &fid); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto error; + } + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + + i_mode = inode->i_mode; + if (S_ISLNK(i_mode)) { + EXFAT_I(inode)->target = kmalloc(i_size_read(inode)+1, GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto error; + } + FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); + *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; + } + + alias = d_find_alias(inode); + if (alias && !exfat_d_anon_disconn(alias)) { + CHECK_ERR(d_unhashed(alias)); + if (!S_ISDIR(i_mode)) + d_move(alias, dentry); + iput(inode); + __unlock_super(sb); + DPRINTK("exfat_lookup exited 1\n"); + return alias; + } else { + dput(alias); + } +out: + __unlock_super(sb); + dentry->d_time = dentry->d_parent->d_inode->i_version; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + dentry->d_op = sb->s_root->d_op; + dentry = d_splice_alias(inode, dentry); + if (dentry) { + dentry->d_op = sb->s_root->d_op; + dentry->d_time = dentry->d_parent->d_inode->i_version; + } +#else + dentry = d_splice_alias(inode, dentry); + if (dentry) + dentry->d_time = dentry->d_parent->d_inode->i_version; +#endif + DPRINTK("exfat_lookup exited 2\n"); + return dentry; + +error: + __unlock_super(sb); + DPRINTK("exfat_lookup exited 3\n"); + return ERR_PTR(err); +} + +static int exfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + DPRINTK("exfat_unlink entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_unlink exited\n"); + return err; +} + +static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + u64 len = (u64) strlen(target); + u64 ret; + + __lock_super(sb); + + DPRINTK("exfat_symlink entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (u8 *) dentry->d_name.name, FM_SYMLINK, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + err = FsWriteFile(dir, &fid, (char *) target, len, &ret); + + if (err) { + FsRemoveFile(dir, &fid); + + if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + EXFAT_I(inode)->target = kmalloc(len+1, GFP_KERNEL); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto out; + } + memcpy(EXFAT_I(inode)->target, target, len+1); + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_symlink exited\n"); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +#else +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_mkdir entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateDir(dir, (u8 *) dentry->d_name.name, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_mkdir exited\n"); + return err; +} + +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + DPRINTK("exfat_rmdir entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -ENOTEMPTY; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_DIRBUSY) + err = -EBUSY; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + DPRINTK("exfat_rmdir exited\n"); + return err; +} + +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + struct timespec ts; + loff_t i_pos; + int err; + + __lock_super(sb); + + DPRINTK("exfat_rename entered\n"); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); + + err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + new_dir->i_version++; + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | + (EXFAT_I(old_inode)->fid.entry & 0xffffffff); + + exfat_detach(old_inode); + exfat_attach(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { + drop_nlink(old_dir); + if (!new_inode) + inc_nlink(new_dir); + } + + old_dir->i_version++; + old_dir->i_ctime = old_dir->i_mtime = ts; + if (IS_DIRSYNC(old_dir)) + (void) exfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + exfat_detach(new_inode); + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + new_inode->i_ctime = ts; + } + +out: + __unlock_super(sb); + DPRINTK("exfat_rename exited\n"); + return err; +} + +static int exfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + err = generic_cont_expand_simple(inode, size); + if (err != 0) + return err; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + + if (IS_SYNC(inode)) { + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + err = (err) ? (err) : (err2); + err2 = write_inode_now(inode, 1); + err = (err) ? (err) : (err2); + if (!err) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) + err = wait_on_page_writeback_range(mapping, + start >> PAGE_CACHE_SHIFT, + (start + count - 1) >> PAGE_CACHE_SHIFT); +#else + err = filemap_fdatawait_range(mapping, start, start + count - 1); +#endif + } + return err; +} + +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (!uid_eq(current_fsuid(), inode->i_uid)) +#else + if (current_fsuid() != inode->i_uid) +#endif + { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO))) + return -EPERM; + + if (exfat_mode_can_hold_ro(inode)) { + /* Of the w bits, either all (subject to umask) or none must be present. */ + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + } else { + /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */ + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +static int exfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + + struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35) + loff_t old_size; +#endif + + DPRINTK("exfat_setattr entered\n"); + + if ((attr->ia_valid & ATTR_SIZE) + && (attr->ia_size > i_size_read(inode))) { + error = exfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + return error; + attr->ia_valid &= ~ATTR_SIZE; + } + + ia_valid = attr->ia_valid; + + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) + && exfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); + } + + error = inode_change_ok(inode, attr); + attr->ia_valid = ia_valid; + if (error) + return error; + + if (((attr->ia_valid & ATTR_UID) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || + ((attr->ia_valid & ATTR_GID) && + (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || +#else + (attr->ia_uid != sbi->options.fs_uid)) || + ((attr->ia_valid & ATTR_GID) && + (attr->ia_gid != sbi->options.fs_gid)) || +#endif + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { + return -EPERM; + } + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + EXFAT_I(inode)->fid.size = i_size_read(inode); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + if (attr->ia_valid) + error = inode_setattr(inode, attr); +#else + if (attr->ia_valid & ATTR_SIZE) { + old_size = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_write(&EXFAT_I(inode)->truncate_lock); + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); + up_write(&EXFAT_I(inode)->truncate_lock); +#else + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); +#endif + } + setattr_copy(inode, attr); + mark_inode_dirty(inode); +#endif + + DPRINTK("exfat_setattr exited\n"); + return error; +} + +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + + DPRINTK("exfat_getattr entered\n"); + + generic_fillattr(inode, stat); + stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; + + DPRINTK("exfat_getattr exited\n"); + return 0; +} + +const struct inode_operations exfat_dir_inode_operations = { + .create = exfat_create, + .lookup = exfat_lookup, + .unlink = exfat_unlink, + .symlink = exfat_symlink, + .mkdir = exfat_mkdir, + .rmdir = exfat_rmdir, + .rename = exfat_rename, + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* File Operations */ +/*======================================================================*/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0) +static const char *exfat_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + if (ei->target != NULL) { + char *cookie = ei->target; + if (cookie != NULL) { + return (char *)(ei->target); + } + } + return NULL; +} +#elif LINUX_VERSION_CODE > KERNEL_VERSION(4,1,0) +static const char *exfat_follow_link(struct dentry *dentry, void **cookie) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + return *cookie = (char *)(ei->target); +} +#else +static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + nd_set_link(nd, (char *)(ei->target)); + return NULL; +} +#endif + +const struct inode_operations exfat_symlink_inode_operations = { + .readlink = generic_readlink, + #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) + .follow_link = exfat_follow_link, + #endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0) + .get_link = exfat_get_link, + #endif +}; + +static int exfat_file_release(struct inode *inode, struct file *filp) +{ + struct super_block *sb = inode->i_sb; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + FsSyncVol(sb, 0); + return 0; +} + +const struct file_operations exfat_file_operations = { + .llseek = generic_file_llseek, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) + .read = new_sync_read, + .write = new_sync_write, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, +#endif + .mmap = generic_file_mmap, + .release = exfat_file_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif + .splice_read = generic_file_splice_read, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + int err; + + __lock_super(sb); + + /* + * This protects against truncating a file bigger than it was then + * trying to write into the hole. + */ + if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + if (EXFAT_I(inode)->fid.start_clu == 0) + goto out; + + err = FsTruncateFile(inode, old_size, i_size_read(inode)); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + if (IS_DIRSYNC(inode)) + (void) exfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; +out: + __unlock_super(sb); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) +static void exfat_truncate(struct inode *inode) +{ + _exfat_truncate(inode, i_size_read(inode)); +} +#endif + +const struct inode_operations exfat_file_inode_operations = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) + .truncate = exfat_truncate, +#endif + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* Address Space Operations */ +/*======================================================================*/ + +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(sbi->bd_info); + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; + sector_t last_block; + int err, clu_offset, sec_offset; + unsigned int cluster; + + *phys = 0; + *mapped_blocks = 0; + + if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) { + if (inode->i_ino == EXFAT_ROOT_INO) { + if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) { + *phys = sector + p_fs->root_start_sector; + *mapped_blocks = 1; + } + return 0; + } + } + + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; + if (sector >= last_block) { + if (*create == 0) + return 0; + } else { + *create = 0; + } + + clu_offset = sector >> p_fs->sectors_per_clu_bits; /* cluster offset */ + sec_offset = sector & (p_fs->sectors_per_clu - 1); /* sector offset in cluster */ + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsMapCluster(inode, clu_offset, &cluster); + + if (err) { + if (err == FFS_FULL) + return -ENOSPC; + else + return -EIO; + } else if (cluster != CLUSTER_32(~0)) { + *phys = START_SECTOR(cluster) + sec_offset; + *mapped_blocks = p_fs->sectors_per_clu - sec_offset; + } + + return 0; +} + +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err; + unsigned long mapped_blocks; + sector_t phys; + + __lock_super(sb); + + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); + if (err) { + __unlock_super(sb); + return err; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + if (create) { + EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; + set_buffer_new(bh_result); + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + __unlock_super(sb); + + return 0; +} + +static int exfat_readpage(struct file *file, struct page *page) +{ + int ret; + ret = mpage_readpage(page, exfat_get_block); + return ret; +} + +static int exfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + int ret; + ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); + return ret; +} + +static int exfat_writepage(struct page *page, struct writeback_control *wbc) +{ + int ret; + ret = block_write_full_page(page, exfat_get_block, wbc); + return ret; +} + +static int exfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + int ret; + ret = mpage_writepages(mapping, wbc, exfat_get_block); + return ret; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) +static void exfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + if (to > i_size_read(inode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0) + truncate_pagecache(inode, i_size_read(inode)); +#else + truncate_pagecache(inode, to, i_size_read(inode)); +#endif + EXFAT_I(inode)->fid.size = i_size_read(inode); + _exfat_truncate(inode, i_size_read(inode)); + } +} +#endif + +static int exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + int ret; + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + exfat_get_block, + &EXFAT_I(mapping->host)->mmu_private); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (ret < 0) + exfat_write_failed(mapping, pos+len); +#endif + return ret; +} + +static int exfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (err < len) + exfat_write_failed(mapping, pos+len); +#endif + + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + fid->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + return err; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) +#ifdef CONFIG_AIO_OPTIMIZATION +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + struct iov_iter *iter, loff_t offset) +#else +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +#endif +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + struct iov_iter *iter, loff_t offset) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) +static ssize_t exfat_direct_IO(struct kiocb *iocb, + struct iov_iter *iter, loff_t offset) +#else /* >= 4.7.x */ +static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +#endif +{ + struct inode *inode = iocb->ki_filp->f_mapping->host; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + struct address_space *mapping = iocb->ki_filp->f_mapping; +#endif + ssize_t ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0) + int rw; + + rw = iov_iter_rw(iter); +#endif + + if (rw == WRITE) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) +#ifdef CONFIG_AIO_OPTIMIZATION + if (EXFAT_I(inode)->mmu_private < + (offset + iov_iter_count(iter))) +#else + if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) +#endif +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + if (EXFAT_I(inode)->mmu_private < (offset + iov_iter_count(iter))) +#else + if (EXFAT_I(inode)->mmu_private < iov_iter_count(iter)) +#endif + return 0; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) + ret = blockdev_direct_IO(iocb, inode, iter, + offset, exfat_get_block); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + ret = blockdev_direct_IO(rw, iocb, inode, iter, + offset, exfat_get_block); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) +#ifdef CONFIG_AIO_OPTIMIZATION + ret = blockdev_direct_IO(rw, iocb, inode, iter, + offset, exfat_get_block); +#else + ret = blockdev_direct_IO(rw, iocb, inode, iov, + offset, nr_segs, exfat_get_block); +#endif +#else + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, exfat_get_block, NULL); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) + if ((ret < 0) && (rw & WRITE)) + exfat_write_failed(mapping, iov_iter_count(iter)); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + if ((ret < 0) && (rw & WRITE)) + exfat_write_failed(mapping, offset+iov_iter_count(iter)); +#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if ((ret < 0) && (rw & WRITE)) +#ifdef CONFIG_AIO_OPTIMIZATION + exfat_write_failed(mapping, offset+iov_iter_count(iter)); +#else + exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); +#endif +#endif + return ret; +} + +static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_read(&EXFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->truncate_lock); +#else + down_read(&EXFAT_I(mapping->host)->i_alloc_sem); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->i_alloc_sem); +#endif + + return blocknr; +} + +const struct address_space_operations exfat_aops = { + .readpage = exfat_readpage, + .readpages = exfat_readpages, + .writepage = exfat_writepage, + .writepages = exfat_writepages, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + .sync_page = block_sync_page, +#endif + .write_begin = exfat_write_begin, + .write_end = exfat_write_end, + .direct_IO = exfat_direct_IO, + .bmap = _exfat_bmap +}; + +/*======================================================================*/ +/* Super Operations */ +/*======================================================================*/ + +static inline unsigned long exfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, EXFAT_HASH_BITS); +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) + struct hlist_node *node; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, node, head, i_hash_fat) { +#else + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { +#endif + CHECK_ERR(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} + +static void exfat_attach(struct inode *inode, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + EXFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +static void exfat_detach(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&EXFAT_I(inode)->i_hash_fat); + EXFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + +/* doesn't deal with root inode */ +static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); + + FsReadStat(inode, &info); + + EXFAT_I(inode)->i_pos = 0; + EXFAT_I(inode)->target = NULL; + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = get_seconds(); + + if (info.Attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00) + set_nlink(inode, info.NumSubdirs); +#else + inode->i_nlink = info.NumSubdirs; +#endif + } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_symlink_inode_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_file_inode_operations; + inode->i_fop = &exfat_file_operations; + inode->i_mapping->a_ops = &exfat_aops; + inode->i_mapping->nrpages = 0; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } + exfat_save_attr(inode, info.Attr); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + + exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); + + return 0; +} + +static struct inode *exfat_build_inode(struct super_block *sb, + FILE_ID_T *fid, loff_t i_pos) { + struct inode *inode; + int err; + + inode = exfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, EXFAT_ROOT_INO); + inode->i_version = 1; + err = exfat_fill_inode(inode, fid); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + exfat_attach(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +static int exfat_sync_inode(struct inode *inode) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) + return exfat_write_inode(inode, 0); +#else + return exfat_write_inode(inode, NULL); +#endif +} + +static struct inode *exfat_alloc_inode(struct super_block *sb) +{ + struct exfat_inode_info *ei; + + ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + init_rwsem(&ei->truncate_lock); +#endif + + return &ei->vfs_inode; +} + +static void exfat_destroy_inode(struct inode *inode) +{ + if (EXFAT_I(inode)->target) + kfree(EXFAT_I(inode)->target); + EXFAT_I(inode)->target = NULL; + + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) +static int exfat_write_inode(struct inode *inode, int wait) +#else +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +#endif +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + DIR_ENTRY_T info; + + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + + info.Attr = exfat_make_attr(inode); + info.Size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + FsWriteStat(inode, &info); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static void exfat_delete_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); +} + +static void exfat_clear_inode(struct inode *inode) +{ + exfat_detach(inode); + remove_inode_hash(inode); +} +#else +static void exfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) + i_size_write(inode, 0); + invalidate_inode_buffers(inode); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) + end_writeback(inode); +#else + clear_inode(inode); +#endif + exfat_detach(inode); + + remove_inode_hash(inode); +} +#endif + +static void exfat_free_super(struct exfat_sb_info *sbi) +{ + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->options.iocharset != exfat_default_iocharset) + kfree(sbi->options.iocharset); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + /* mutex_init is in exfat_fill_super function. only for 3.7+ */ + mutex_destroy(&sbi->s_lock); +#endif + kfree(sbi); +} + +static void exfat_put_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + if (__is_sb_dirty(sb)) + exfat_write_super(sb); + + FsUmountVol(sb); + + sb->s_fs_info = NULL; + exfat_free_super(sbi); +} + +static void exfat_write_super(struct super_block *sb) +{ + __lock_super(sb); + + __set_sb_clean(sb); + + if (!(sb->s_flags & MS_RDONLY)) + FsSyncVol(sb, 1); + + __unlock_super(sb); +} + +static int exfat_sync_fs(struct super_block *sb, int wait) +{ + int err = 0; + + if (__is_sb_dirty(sb)) { + __lock_super(sb); + __set_sb_clean(sb); + err = FsSyncVol(sb, 1); + __unlock_super(sb); + } + + return err; +} + +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + VOL_INFO_T info; + + if (p_fs->used_clusters == (u32) ~0) { + if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) + return -EIO; + + } else { + info.FatType = p_fs->vol_type; + info.ClusterSize = p_fs->cluster_size; + info.NumClusters = p_fs->num_clusters - 2; + info.UsedClusters = p_fs->used_clusters; + info.FreeClusters = info.NumClusters - info.UsedClusters; + + if (p_fs->dev_ejected) + printk("[EXFAT] statfs on device is ejected\n"); + } + + buf->f_type = sb->s_magic; + buf->f_bsize = info.ClusterSize; + buf->f_blocks = info.NumClusters; + buf->f_bfree = info.FreeClusters; + buf->f_bavail = info.FreeClusters; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int exfat_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) +static int exfat_show_options(struct seq_file *m, struct dentry *root) +{ + struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); +#else +static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); +#endif + struct exfat_mount_options *opts = &sbi->options; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (__kuid_val(opts->fs_uid)) + seq_printf(m, ",uid=%u", __kuid_val(opts->fs_uid)); + if (__kgid_val(opts->fs_gid)) + seq_printf(m, ",gid=%u", __kgid_val(opts->fs_gid)); +#else + if (opts->fs_uid != 0) + seq_printf(m, ",uid=%u", opts->fs_uid); + if (opts->fs_gid != 0) + seq_printf(m, ",gid=%u", opts->fs_gid); +#endif + seq_printf(m, ",fmask=%04o", opts->fs_fmask); + seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_disk) + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + seq_printf(m, ",namecase=%u", opts->casesensitive); + if (opts->errors == EXFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == EXFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); +#ifdef CONFIG_EXFAT_DISCARD + if (opts->discard) + seq_printf(m, ",discard"); +#endif + return 0; +} + +const struct super_operations exfat_sops = { + .alloc_inode = exfat_alloc_inode, + .destroy_inode = exfat_destroy_inode, + .write_inode = exfat_write_inode, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .delete_inode = exfat_delete_inode, + .clear_inode = exfat_clear_inode, +#else + .evict_inode = exfat_evict_inode, +#endif + .put_super = exfat_put_super, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + .write_super = exfat_write_super, +#endif + .sync_fs = exfat_sync_fs, + .statfs = exfat_statfs, + .remount_fs = exfat_remount, + .show_options = exfat_show_options, +}; + +/*======================================================================*/ +/* Super Block Read Operations */ +/*======================================================================*/ + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_codepage, + Opt_charset, + Opt_namecase, + Opt_debug, + Opt_err_cont, + Opt_err_panic, + Opt_err_ro, + Opt_utf8_hack, + Opt_err, +#ifdef CONFIG_EXFAT_DISCARD + Opt_discard, +#endif /* EXFAT_CONFIG_DISCARD */ +}; + +static const match_table_t exfat_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_dmask, "dmask=%o"}, + {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, + {Opt_codepage, "codepage=%u"}, + {Opt_charset, "iocharset=%s"}, + {Opt_namecase, "namecase=%u"}, + {Opt_debug, "debug"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_panic, "errors=panic"}, + {Opt_err_ro, "errors=remount-ro"}, + {Opt_utf8_hack, "utf8"}, +#ifdef CONFIG_EXFAT_DISCARD + {Opt_discard, "discard"}, +#endif /* CONFIG_EXFAT_DISCARD */ + {Opt_err, NULL} +}; + +static int parse_options(char *options, int silent, int *debug, + struct exfat_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *iocharset; + + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = (unsigned short) -1; + opts->codepage = exfat_default_codepage; + opts->iocharset = exfat_default_iocharset; + opts->casesensitive = 0; + opts->errors = EXFAT_ERRORS_RO; +#ifdef CONFIG_EXFAT_DISCARD + opts->discard = 0; +#endif + *debug = 0; + + if (!options) + goto out; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, exfat_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_uid = KUIDT_INIT(option); +#else + opts->fs_uid = option; +#endif + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_gid = KGIDT_INIT(option); +#else + opts->fs_gid = option; +#endif + break; + case Opt_umask: + case Opt_dmask: + case Opt_fmask: + if (match_octal(&args[0], &option)) + return 0; + if (token != Opt_dmask) + opts->fs_fmask = option; + if (token != Opt_fmask) + opts->fs_dmask = option; + break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + return 0; + opts->codepage = option; + break; + case Opt_charset: + if (opts->iocharset != exfat_default_iocharset) + kfree(opts->iocharset); + iocharset = match_strdup(&args[0]); + if (!iocharset) + return -ENOMEM; + opts->iocharset = iocharset; + break; + case Opt_namecase: + if (match_int(&args[0], &option)) + return 0; + opts->casesensitive = option; + break; + case Opt_err_cont: + opts->errors = EXFAT_ERRORS_CONT; + break; + case Opt_err_panic: + opts->errors = EXFAT_ERRORS_PANIC; + break; + case Opt_err_ro: + opts->errors = EXFAT_ERRORS_RO; + break; + case Opt_debug: + *debug = 1; + break; +#ifdef CONFIG_EXFAT_DISCARD + case Opt_discard: + opts->discard = 1; + break; +#endif /* CONFIG_EXFAT_DISCARD */ + case Opt_utf8_hack: + break; + default: + if (!silent) + printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); + return -EINVAL; + } + } + +out: + if (opts->allow_utime == (unsigned short) -1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + + return 0; +} + +static void exfat_hash_init(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < EXFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int exfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct timespec ts; + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; + EXFAT_I(inode)->fid.dir.flags = 0x01; + EXFAT_I(inode)->fid.entry = -1; + EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; + EXFAT_I(inode)->fid.flags = 0x01; + EXFAT_I(inode)->fid.type = TYPE_DIR; + EXFAT_I(inode)->fid.rwoffset = 0; + EXFAT_I(inode)->fid.hint_last_off = -1; + + EXFAT_I(inode)->target = NULL; + + FsReadStat(inode, &info); + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = 0; + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + exfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,00) + set_nlink(inode, info.NumSubdirs + 2); +#else + inode->i_nlink = info.NumSubdirs + 2; +#endif + + return 0; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) +static void setup_dops(struct super_block *sb) +{ + if (EXFAT_SB(sb)->options.casesensitive == 0) + sb->s_d_op = &exfat_ci_dentry_ops; + else + sb->s_d_op = &exfat_dentry_ops; +} +#endif + +static int exfat_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode = NULL; + struct exfat_sb_info *sbi; + int debug, ret; + long error; + char buf[50]; + + /* + * GFP_KERNEL is ok here, because while we do hold the + * supeblock lock, memory pressure can't call back into + * the filesystem, since we're only just about to mount + * it and have no inodes etc active! + */ + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + mutex_init(&sbi->s_lock); +#endif + sb->s_fs_info = sbi; + sb->s_flags |= MS_NODIRATIME; + sb->s_magic = EXFAT_SUPER_MAGIC; + sb->s_op = &exfat_sops; + + error = parse_options(data, silent, &debug, &sbi->options); + if (error) + goto out_fail; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37) + setup_dops(sb); +#endif + + error = -EIO; + sb_min_blocksize(sb, 512); + sb->s_maxbytes = 0x7fffffffffffffffLL; /* maximum file size */ + + ret = FsMountVol(sb); + if (ret) { + if (!silent) + printk(KERN_ERR "[EXFAT] FsMountVol failed\n"); + + goto out_fail; + } + + /* set up enough so that it can read an inode */ + exfat_hash_init(sb); + + /* + * The low byte of FAT's first entry must have same value with + * media-field. But in real world, too many devices is + * writing wrong value. So, removed that validity check. + * + * if (FAT_FIRST_ENT(sb, media) != first) + */ + + /* codepage is not meaningful in exfat */ + if (sbi->fs_info.vol_type != EXFAT) { + error = -EINVAL; + sprintf(buf, "cp%d", sbi->options.codepage); + sbi->nls_disk = load_nls(buf); + if (!sbi->nls_disk) { + printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf); + goto out_fail2; + } + } + + sbi->nls_io = load_nls(sbi->options.iocharset); + + error = -ENOMEM; + root_inode = new_inode(sb); + if (!root_inode) + goto out_fail2; + root_inode->i_ino = EXFAT_ROOT_INO; + root_inode->i_version = 1; + error = exfat_read_root(root_inode); + if (error < 0) + goto out_fail2; + error = -ENOMEM; + exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + sb->s_root = d_make_root(root_inode); +#else + sb->s_root = d_alloc_root(root_inode); +#endif + if (!sb->s_root) { + printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); + goto out_fail2; + } + + return 0; + +out_fail2: + FsUmountVol(sb); +out_fail: + if (root_inode) + iput(root_inode); + sb->s_fs_info = NULL; + exfat_free_super(sbi); + return error; +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +static int exfat_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt); +} +#else +static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) { + return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); +} +#endif + +static void init_once(void *foo) +{ + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init exfat_init_inodecache(void) +{ + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", + sizeof(struct exfat_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); + if (exfat_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void __exit exfat_destroy_inodecache(void) +{ + kmem_cache_destroy(exfat_inode_cachep); +} + +#ifdef CONFIG_EXFAT_KERNEL_DEBUG +static void exfat_debug_kill_sb(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct block_device *bdev = sb->s_bdev; + + long flags; + + if (sbi) { + flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { + /* invalidate_bdev drops all device cache include dirty. + we use this to simulate device removal */ + FsReleaseCache(sb); + invalidate_bdev(bdev); + } + } + + kill_block_super(sb); +} +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, +#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE) + .name = "texfat", +#else + .name = "exfat", +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + .get_sb = exfat_get_sb, +#else + .mount = exfat_fs_mount, +#endif +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + .kill_sb = exfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_exfat(void) +{ + int err; + + err = FsInit(); + if (err) { + if (err == FFS_MEMORYERR) + return -ENOMEM; + else + return -EIO; + } + + printk(KERN_INFO "exFAT: Version %s\n", EXFAT_VERSION); + + err = exfat_init_inodecache(); + if (err) + goto out; + + err = register_filesystem(&exfat_fs_type); + if (err) + goto out; + + return 0; +out: + FsShutdown(); + return err; +} + +static void __exit exit_exfat(void) +{ + exfat_destroy_inodecache(); + unregister_filesystem(&exfat_fs_type); + FsShutdown(); +} + +module_init(init_exfat); +module_exit(exit_exfat); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("exFAT Filesystem Driver"); +#ifdef MODULE_ALIAS_FS +#if defined(CONFIG_MACH_LGE) || defined(CONFIG_HTC_BATT_CORE) +MODULE_ALIAS_FS("texfat"); +#else +MODULE_ALIAS_FS("exfat"); +#endif +#endif diff --git a/fs/exfat/exfat_super.h b/fs/exfat/exfat_super.h new file mode 100755 index 000000000000..916811e3d31e --- /dev/null +++ b/fs/exfat/exfat_super.h @@ -0,0 +1,171 @@ +/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _EXFAT_LINUX_H +#define _EXFAT_LINUX_H + +#include +#include +#include +#include +#include +#include + +#include "exfat_config.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_core.h" + +#define EXFAT_ERRORS_CONT 1 /* ignore error and continue */ +#define EXFAT_ERRORS_PANIC 2 /* panic on error */ +#define EXFAT_ERRORS_RO 3 /* remount r/o on error */ + +/* ioctl command */ +#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) + +struct exfat_mount_options { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + kuid_t fs_uid; + kgid_t fs_gid; +#else + uid_t fs_uid; + gid_t fs_gid; +#endif + unsigned short fs_fmask; + unsigned short fs_dmask; + unsigned short allow_utime; /* permission for setting the [am]time */ + unsigned short codepage; /* codepage for shortname conversions */ + char *iocharset; /* charset for filename input/display */ + unsigned char casesensitive; + unsigned char errors; /* on error: continue, panic, remount-ro */ +#ifdef CONFIG_EXFAT_DISCARD + unsigned char discard; /* flag on if -o dicard specified and device support discard() */ +#endif /* CONFIG_EXFAT_DISCARD */ +}; + +#define EXFAT_HASH_BITS 8 +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) + +/* + * EXFAT file system in-core superblock data + */ +struct exfat_sb_info { + FS_INFO_T fs_info; + BD_INFO_T bd_info; + + struct exfat_mount_options options; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + int s_dirt; + struct mutex s_lock; +#endif + struct nls_table *nls_disk; /* Codepage used on disk */ + struct nls_table *nls_io; /* Charset used for input and display */ + + struct inode *fat_inode; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; +#ifdef CONFIG_EXFAT_KERNEL_DEBUG + long debug_flags; +#endif /* CONFIG_EXFAT_KERNEL_DEBUG */ +}; + +/* + * EXFAT file system inode data in memory + */ +struct exfat_inode_info { + FILE_ID_T fid; + char *target; + /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */ + loff_t mmu_private; /* physically allocated size */ + loff_t i_pos; /* on-disk position of directory entry or 0 */ + struct hlist_node i_hash_fat; /* hash by i_location */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + struct rw_semaphore truncate_lock; +#endif + struct inode vfs_inode; + struct rw_semaphore i_alloc_sem; /* protect bmap against truncate */ +}; + +#define EXFAT_SB(sb) ((struct exfat_sb_info *)((sb)->s_fs_info)) + +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) +{ + return container_of(inode, struct exfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int exfat_mode_can_hold_ro(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & S_IWUGO) + return 1; + return 0; +} + +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, + u32 attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~S_IWUGO; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + else if (attr & ATTR_SYMLINK) + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; + else + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline u32 exfat_make_attr(struct inode *inode) +{ + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) + return (EXFAT_I(inode)->fid.attr) | ATTR_READONLY; + else + return EXFAT_I(inode)->fid.attr; +} + +static inline void exfat_save_attr(struct inode *inode, u32 attr) +{ + if (exfat_mode_can_hold_ro(inode)) + EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; + else + EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); +} + +#endif /* _EXFAT_LINUX_H */ diff --git a/fs/exfat/exfat_upcase.c b/fs/exfat/exfat_upcase.c new file mode 100755 index 000000000000..3807f37caacb --- /dev/null +++ b/fs/exfat/exfat_upcase.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_upcase.c */ +/* PURPOSE : exFAT Up-case Table */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" + +#include "exfat_nls.h" + +const u8 uni_upcase[NUM_UPCASE<<1] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; diff --git a/fs/exfat/exfat_version.h b/fs/exfat/exfat_version.h new file mode 100755 index 000000000000..a93fa46be04e --- /dev/null +++ b/fs/exfat/exfat_version.h @@ -0,0 +1,19 @@ +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_version.h */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY */ +/* */ +/* - 2012.02.10 : Release Version 1.1.0 */ +/* - 2012.04.02 : P1 : Change Module License to Samsung Proprietary */ +/* - 2012.06.07 : P2 : Fixed incorrect filename problem */ +/* */ +/************************************************************************/ + +#define EXFAT_VERSION "1.2.9" diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 3a2594665b44..844805ebfaf8 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -691,13 +691,19 @@ encryption_policy_out: struct ext4_encryption_policy policy; int err = 0; - if (!ext4_encrypted_inode(inode)) + if (!ext4_encrypted_inode(inode)) { + pr_err("EXT4_IOC_GET_ENCRYPTION_POLICY: no entry!\n"); return -ENOENT; + } err = ext4_get_policy(inode, &policy); - if (err) + if (err) { + pr_err("EXT4_IOC_GET_ENCRYPTION_POLICY: get policy failed:%d \n", err); return err; - if (copy_to_user((void __user *)arg, &policy, sizeof(policy))) + } + if (copy_to_user((void __user *)arg, &policy, sizeof(policy))) { + pr_err("EXT4_IOC_GET_ENCRYPTION_POLICY: copy data failed!\n"); return -EFAULT; + } return 0; #else return -EOPNOTSUPP; diff --git a/fs/file.c b/fs/file.c index 39f8f15921da..b04922dac77c 100644 --- a/fs/file.c +++ b/fs/file.c @@ -23,6 +23,8 @@ #include #include +#define FD_OVER_CHECK + int sysctl_nr_open __read_mostly = 1024*1024; int sysctl_nr_open_min = BITS_PER_LONG; /* our max() is unusable in constant expressions ;-/ */ @@ -490,6 +492,138 @@ static unsigned long find_next_fd(struct fdtable *fdt, unsigned long start) return find_next_zero_bit(fdt->open_fds, maxfd, start); } +#ifdef FD_OVER_CHECK +#define FD_CHECK_NAME_SIZE 256 +/* Declare a radix tree to construct fd set tree */ +static RADIX_TREE(over_fd_tree, GFP_KERNEL); +static LIST_HEAD(fd_listhead); +static DEFINE_MUTEX(over_fd_mutex); +struct over_fd_entry +{ + int num_of_fd; + char name[FD_CHECK_NAME_SIZE]; + int hash; + struct list_head fd_link; +}; + +/* +* Get File Name from FD value +*/ +long get_file_name_from_fd(struct files_struct *files, int fd, int procid, struct over_fd_entry *res_name) +{ + char *tmp; + char *pathname; + struct file *file; + struct path path; + spin_lock(&files->file_lock); + file = fget(fd); + if (!file) { + spin_unlock(&files->file_lock); + return (long)NULL; + } + path_get(&file->f_path); + path = file->f_path; + fput(file); + spin_unlock(&files->file_lock); + tmp = (char *)__get_free_page(GFP_TEMPORARY); + if (!tmp) { + return (long)NULL; + } + pathname = d_path(&path, tmp, PAGE_SIZE); + + path_put(&path); + if (IS_ERR(pathname)) + { + free_page((unsigned long)tmp); + return PTR_ERR(pathname); + } /* do something here with pathname */ + if(pathname!=NULL) + { + strncpy(res_name->name, pathname, FD_CHECK_NAME_SIZE - 1); + } + free_page((unsigned long)tmp); + return 1; +} + +unsigned int get_hash(char *name) +{ + return full_name_hash(name, strlen(name)); +} + +static struct over_fd_entry* fd_lookup(unsigned int hash) +{ + return radix_tree_lookup(&over_fd_tree, hash); +} + +static void fd_insert(struct over_fd_entry *entry) +{ + unsigned int hash = get_hash(entry->name); + struct over_fd_entry *find_entry = fd_lookup(hash); + + if(!find_entry) // Can't find the element, just add the element + { + entry->num_of_fd = 1; + entry->hash = hash; + list_add_tail(&entry->fd_link, &fd_listhead); + radix_tree_insert(&over_fd_tree, hash, (void *)entry); + } + else // Cover the original element + { + find_entry->num_of_fd = find_entry->num_of_fd+1; + kfree(entry); + } +} + +static void fd_delete(unsigned int hash) +{ + radix_tree_delete(&over_fd_tree, hash); +} + +void fd_show_open_files(pid_t pid, struct files_struct *files, struct fdtable *fdt) +{ + int i=0; + struct over_fd_entry *lentry; + long result; + int num_of_entry; + int sum_fds_of_pid = 0; + + mutex_lock(&over_fd_mutex); + //printk(KERN_ERR "(PID:%d)Max FD Number:%d", current->pid, fdt->max_fds); + for(i=0; imax_fds; i++) { + struct over_fd_entry *entry = (struct over_fd_entry*)kzalloc(sizeof(struct over_fd_entry), GFP_KERNEL); + if(!entry) { + printk("[FD_LEAK](PID:%d)Empty FD:%d", pid, i); + } + else { + memset(entry->name, 0, sizeof entry->name); + result = get_file_name_from_fd(files, i, pid, entry); + if(result==1) { + fd_insert(entry); + sum_fds_of_pid++; + } + } + } + for(;;) { + if(list_empty(&fd_listhead)) { + break; + } + lentry = list_entry((&fd_listhead)->next, struct over_fd_entry, fd_link); + num_of_entry = lentry->num_of_fd; + if(lentry != NULL && lentry->name!=NULL) + printk("[FD_LEAK]OverAllocFDError(PID:%d fileName:%s Num:%d)\n", pid, lentry->name, num_of_entry); + else + printk("[FD_LEAK]OverAllocFDError(PID:%d fileName:%s Num:%d)\n", pid, "NULL", num_of_entry); + list_del((&fd_listhead)->next); + fd_delete(lentry->hash); + kfree(lentry); + } + if(sum_fds_of_pid) { + printk("[FD_LEAK]OverAllocFDError(PID:%d totalFDs:%d)\n", pid, sum_fds_of_pid); + } + mutex_unlock(&over_fd_mutex); +} +#endif + /* * allocate a file descriptor, mark it busy. */ @@ -548,6 +682,17 @@ repeat: out: spin_unlock(&files->file_lock); + #ifdef FD_OVER_CHECK + if(error == -EMFILE) { + // every process just dump only one time + if(current->group_leader->dump_fd_leak==false) + { + current->group_leader->dump_fd_leak = true; + printk("[FD_LEAK](PID:%d)fd over RLIMIT_NOFILE:%ld", current->pid, rlimit(RLIMIT_NOFILE)); + fd_show_open_files(current->pid, files, fdt); + } + } + #endif return error; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index f0de8fe294f4..053bdf281975 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -19,6 +19,9 @@ #include #include +#include +#include + static const struct file_operations fuse_direct_io_file_operations; static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, @@ -1246,6 +1249,63 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ssize_t err; loff_t endbyte = 0; + struct kstatfs statfs; + u64 avail; + size_t size; + u32 reserved_blocks; + u32 reserved_bytes; + struct path data_partition_path; + + reserved_bytes = get_fuse_conn(inode)->reserved_mem << 20; + + if (reserved_bytes != 0) { + + err = kern_path("/data", + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &data_partition_path); + if (unlikely(err)) + { + printk(KERN_INFO "Failed to get data partition path(%d)\n", + (int)err); + err = vfs_statfs(&file->f_path, &statfs); + if (unlikely(err)) + { + printk(KERN_ERR "statfs file path error(%d)\n", + (int)err); + return err; + } + } + else + { + err = vfs_statfs(&data_partition_path, &statfs); + if (unlikely(err)) + { + printk(KERN_INFO "statfs data partition error(%d)\n", + (int)err); + err = vfs_statfs(&file->f_path, &statfs); + if (unlikely(err)) + { + path_put(&data_partition_path); + return err; + } + } + path_put(&data_partition_path); + } + + reserved_blocks = (reserved_bytes / statfs.f_bsize); + + if (statfs.f_bavail < reserved_blocks) + statfs.f_bavail = 0; + else + statfs.f_bavail -= reserved_blocks; + + avail = statfs.f_bavail * statfs.f_bsize; + size = iov_length(from->iov, from->nr_segs); + + if ((u64)size > avail) { + return -ENOSPC; + } + } + if (get_fuse_conn(inode)->writeback_cache) { /* Update size (EOF optimization) and mode (SUID clearing) */ err = fuse_update_attributes(mapping->host, NULL, file, NULL); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1cc0dce47a2f..519c061ba18e 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -649,6 +649,8 @@ struct fuse_conn { /** number of dentries used in the above array */ int ctl_ndents; + unsigned reserved_mem; + /** Key for lock owner ID scrambling */ u32 scramble_key[4]; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index e04db24ed164..b15adc7cc6a5 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -69,6 +69,7 @@ struct fuse_mount_data { unsigned flags; unsigned max_read; unsigned blksize; + unsigned reserved_mem; }; struct fuse_forget_link *fuse_alloc_forget(void) @@ -391,6 +392,34 @@ static void fuse_put_super(struct super_block *sb) fuse_conn_put(fc); } +static int handle_reserved_statfs(struct kstatfs *stbuf, u32 reserved_mem) +{ + u32 reserved_blocks; + + if(stbuf->f_bsize == 0){ + printk(KERN_ERR "Invalid fuse statfs informations, block size is 0\n"); + return -EINVAL; + } + + reserved_blocks = ((reserved_mem * 1024 * 1024)/stbuf->f_bsize); + + stbuf->f_blocks -= reserved_blocks; + + if(stbuf->f_bfree < reserved_blocks) { + stbuf->f_bfree = 0; + } else { + stbuf->f_bfree -= reserved_blocks; + } + + if(stbuf->f_bavail < reserved_blocks) { + stbuf->f_bavail = 0; + } else { + stbuf->f_bavail -= reserved_blocks; + } + + return 0; +} + static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) { stbuf->f_type = FUSE_SUPER_MAGIC; @@ -428,6 +457,10 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) err = fuse_simple_request(fc, &args); if (!err) convert_fuse_statfs(buf, &outarg.st); + + if(!err && fc->reserved_mem != 0) + handle_reserved_statfs(buf,fc->reserved_mem); + return err; } @@ -440,6 +473,7 @@ enum { OPT_ALLOW_OTHER, OPT_MAX_READ, OPT_BLKSIZE, + OPT_RESERVED_MEM, OPT_ERR }; @@ -452,6 +486,7 @@ static const match_table_t tokens = { {OPT_ALLOW_OTHER, "allow_other"}, {OPT_MAX_READ, "max_read=%u"}, {OPT_BLKSIZE, "blksize=%u"}, + {OPT_RESERVED_MEM, "reserved_mem=%u"}, {OPT_ERR, NULL} }; @@ -537,6 +572,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) d->blksize = value; break; + case OPT_RESERVED_MEM: + if (match_int(&args[0], &value)) + return 0; + d->reserved_mem = value; + break; + default: return 0; } @@ -564,6 +605,10 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) seq_printf(m, ",max_read=%u", fc->max_read); if (sb->s_bdev && sb->s_blocksize != FUSE_DEFAULT_BLKSIZE) seq_printf(m, ",blksize=%lu", sb->s_blocksize); + + if(fc->reserved_mem != 0) + seq_printf(m, ",reserved_mem=%uMB",fc->reserved_mem); + return 0; } @@ -1109,6 +1154,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fc->group_id = d.group_id; fc->max_read = max_t(unsigned, 4096, d.max_read); + fc->reserved_mem = d.reserved_mem; + /* Used by get_root_inode() */ sb->s_fs_info = fc; diff --git a/fs/proc/base.c b/fs/proc/base.c index 4c0989076151..8278052594ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1091,15 +1091,9 @@ static ssize_t oom_adj_write(struct file *file, const char __user *buf, goto out; } - task_lock(task); - if (!task->mm) { - err = -EINVAL; - goto err_task_lock; - } - if (!lock_task_sighand(task, &flags)) { err = -ESRCH; - goto err_task_lock; + goto err_put_task; } /* @@ -1129,8 +1123,7 @@ static ssize_t oom_adj_write(struct file *file, const char __user *buf, trace_oom_score_adj_update(task); err_sighand: unlock_task_sighand(task, &flags); -err_task_lock: - task_unlock(task); +err_put_task: put_task_struct(task); out: return err < 0 ? err : count; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c old mode 100644 new mode 100755 index 99c4ffaa43a8..a52d6fb714b4 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -709,6 +709,70 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) memset(&mss, 0, sizeof mss); /* mmap_sem is held in m_start */ walk_page_vma(vma, &smaps_walk); + if (strcmp(current->comm, "android.bg") == 0) { + if ((unsigned long)(mss.pss >> (10 + PSS_SHIFT)) > 0) { + seq_printf(m, + "Pss: %8lu kB\n", + (unsigned long)(mss.pss >> (10 + PSS_SHIFT))); + } + if ((mss.private_clean >> 10) > 0) { + seq_printf(m, + "Private_Clean: %8lu kB\n", + mss.private_clean >> 10); + } + if ((mss.private_dirty >> 10) > 0) { + seq_printf(m, + "Private_Dirty: %8lu kB\n", + mss.private_dirty >> 10); + } + if ((unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT)) > 0) { + seq_printf(m, + "SwapPss: %8lu kB\n", + (unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT))); + } + m_cache_vma(m, vma); + return 0; + } + if (strcmp(current->comm, "system_server") == 0) { + if ((unsigned long)(mss.pss >> (10 + PSS_SHIFT)) > 0) { + seq_printf(m, + "Pss: %8lu kB\n", + (unsigned long)(mss.pss >> (10 + PSS_SHIFT))); + } + if ((mss.private_clean >> 10) > 0) { + seq_printf(m, + "Private_Clean: %8lu kB\n", + mss.private_clean >> 10); + } + if ((mss.private_dirty >> 10) > 0) { + seq_printf(m, + "Private_Dirty: %8lu kB\n", + mss.private_dirty >> 10); + } + if ((unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT)) > 0) { + seq_printf(m, + "SwapPss: %8lu kB\n", + (unsigned long)(mss.swap_pss >> (10 + PSS_SHIFT))); + } + if ((unsigned long)(mss.shared_clean >>10 ) > 0) { + seq_printf(m, + "Shared_Clean: %8lu kB\n", + (unsigned long)(mss.shared_clean >> 10 ) ); + } + if ((unsigned long)(mss.shared_dirty >> 10) > 0) { + seq_printf(m, + "Shared_Dirty: %8lu kB\n", + (unsigned long)(mss.shared_dirty >>10)); + } + + if ((unsigned long)(mss.swap >> 10) > 0) { + seq_printf(m, + "Swap: %8lu kB\n", + (unsigned long)(mss.swap >> 10) ); + } + m_cache_vma(m, vma); + return 0; + } show_map_vma(m, vma, is_pid); diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 360ae43f590c..6122e141c697 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -44,6 +44,13 @@ config PSTORE_FTRACE If unsure, say N. +config PSTORE_DEVICE_INFO + bool "Log user space messages" + depends on PSTORE + help + add device_info.txt for ramdump. + If unsure, say N. + config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile index b8803cc07fce..c9033b4c2e5d 100644 --- a/fs/pstore/Makefile +++ b/fs/pstore/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_PSTORE) += pstore.o pstore-objs += inode.o platform.o pstore-$(CONFIG_PSTORE_FTRACE) += ftrace.o +pstore-$(CONFIG_PSTORE_DEVICE_INFO) += device_info.o + pstore-$(CONFIG_PSTORE_PMSG) += pmsg.o ramoops-objs += ram.o ram_core.o diff --git a/fs/pstore/device_info.c b/fs/pstore/device_info.c new file mode 100644 index 000000000000..0ff679e531e9 --- /dev/null +++ b/fs/pstore/device_info.c @@ -0,0 +1,122 @@ + +#include + +#include + +#include + +#include + +#include + +static int __init device_info_init(void) +{ + int i, j, target_len; + char *substr, *target_str; + + for (i = 0; i < MAX_ITEM; i++) { + substr = strnstr(boot_command_line, + cmdline_info[i], + sizeof(cmdline_info[i])); + if (substr != NULL) + substr += strlen(cmdline_info[i]); + else + continue; + + if (i == serialno) { + target_str = oem_serialno; + target_len = sizeof(oem_serialno); + } else if (i == hw_version) { + target_str = oem_hw_version; + target_len = sizeof(oem_hw_version); + } else if (i == rf_version) { + target_str = oem_rf_version; + target_len = sizeof(oem_rf_version); + } else if (i == pcba_number) { + target_str = oem_pcba_number; + target_len = sizeof(oem_pcba_number); + } else + continue; + + for (j = 0; substr[j] != ' ' && j < target_len; j++) + target_str[j] = substr[j]; + /*target_str[j] = '\0';*/ + + } + return 1; +} + + + + +static void __init pstore_write_device_info(const char *s, unsigned c) +{ + const char *e = s + c; + + if (c <= 0) + return; + + while (s < e) { + unsigned long flags; + u64 id; + + if (c > psinfo->bufsize) + c = psinfo->bufsize; + + if (oops_in_progress) { + if (!spin_trylock_irqsave(&psinfo->buf_lock, flags)) + break; + } else { + spin_lock_irqsave(&psinfo->buf_lock, flags); + } + memcpy(psinfo->buf, s, c); + psinfo->write(PSTORE_TYPE_DEVICE_INFO, 0, + &id, 0, 0, 0, c, psinfo); + spin_unlock_irqrestore(&psinfo->buf_lock, flags); + s += c; + c = e - s; + } + + return; + +} + +static void __init write_device_info(const char *key, const char *value) +{ + pstore_write_device_info(key, strlen(key)); + pstore_write_device_info(": ", 2); + pstore_write_device_info(value, strlen(value)); + pstore_write_device_info("\r\n", 2); +} + +static int __init init_device_info(void) +{ + + device_info_init(); + pstore_write_device_info(" * * * begin * * * \r\n", + strlen(" * * * begin * * * \r\n")); + + write_device_info("hardware version", oem_hw_version); + write_device_info("rf version", oem_rf_version); + write_device_info("ddr info", ddr_manufacture_and_fw_verion); + write_device_info("pcba number", oem_pcba_number); + write_device_info("serial number", oem_serialno); + + memset(oem_serialno, 0, sizeof(oem_serialno)); + scnprintf(oem_serialno, sizeof(oem_serialno), "%x", chip_serial_num); + write_device_info("socinfo serial_number", oem_serialno); + + write_device_info("ufs vendor and rev", ufs_vendor_and_rev); + + write_device_info("kernel version", linux_banner); + write_device_info("boot command", saved_command_line); + + pstore_write_device_info(" * * * end * * * \r\n", + strlen(" * * * end * * * \r\n")); + + + return 0; +} + +late_initcall(init_device_info); + diff --git a/fs/pstore/device_info.h b/fs/pstore/device_info.h new file mode 100644 index 000000000000..d142b3e577bc --- /dev/null +++ b/fs/pstore/device_info.h @@ -0,0 +1,32 @@ +#ifndef __DEVICE_INFO_H__ +#define __DEVICE_INFO_H__ +extern struct pstore_info *psinfo; + +extern uint32_t chip_serial_num; +extern char ufs_vendor_and_rev[32]; +extern char ddr_manufacture_and_fw_verion[40]; + + +#define MAX_ITEM 4 +#define MAX_LENGTH 32 + +enum { + serialno = 0, + hw_version, + rf_version, + pcba_number +}; + +static char oem_serialno[16] = {0}; +static char oem_hw_version[3] = {0}; +static char oem_rf_version[3] = {0}; +static char oem_pcba_number[30] = {0}; + +const char cmdline_info[MAX_ITEM][MAX_LENGTH] = { + "androidboot.serialno=", + "androidboot.hw_version=", + "androidboot.rf_version=", + "androidboot.pcba_number=", +}; + +#endif diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index ac6c78fe19cf..b92e6d1a6c44 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -178,6 +178,7 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence) } static const struct file_operations pstore_file_operations = { + .owner = THIS_MODULE, .open = pstore_file_open, .read = pstore_file_read, .llseek = pstore_file_llseek, @@ -347,6 +348,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, case PSTORE_TYPE_FTRACE: scnprintf(name, sizeof(name), "ftrace-%s-%lld", psname, id); break; + case PSTORE_TYPE_DEVICE_INFO: + scnprintf(name, sizeof(name), + "device-info-%s-%lld", psname, id); + break; case PSTORE_TYPE_MCE: scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id); break; diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index c9e4bc47c79d..533f4b11d5e4 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -45,7 +45,7 @@ module_param(record_size, ulong, 0400); MODULE_PARM_DESC(record_size, "size of each dump done on oops/panic"); -static ulong ramoops_console_size = MIN_MEM_SIZE; +static ulong ramoops_console_size = 256*1024UL; module_param_named(console_size, ramoops_console_size, ulong, 0400); MODULE_PARM_DESC(console_size, "size of kernel console log"); @@ -53,7 +53,12 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE; module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); -static ulong ramoops_pmsg_size = MIN_MEM_SIZE; +static ulong ramoops_device_info_size = MIN_MEM_SIZE; +module_param_named(device_info_size, ramoops_device_info_size, ulong, 0400); +MODULE_PARM_DESC(device_info_size, "size of device info"); + +static ulong ramoops_pmsg_size = 256*1024UL; + module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); MODULE_PARM_DESC(pmsg_size, "size of user space message log"); @@ -88,6 +93,7 @@ struct ramoops_context { struct persistent_ram_zone **przs; struct persistent_ram_zone *cprz; struct persistent_ram_zone *fprz; + struct persistent_ram_zone *dprz; struct persistent_ram_zone *mprz; phys_addr_t phys_addr; unsigned long size; @@ -95,6 +101,7 @@ struct ramoops_context { size_t record_size; size_t console_size; size_t ftrace_size; + size_t device_info_size; size_t pmsg_size; int dump_oops; struct persistent_ram_ecc_info ecc_info; @@ -104,6 +111,7 @@ struct ramoops_context { unsigned int dump_read_cnt; unsigned int console_read_cnt; unsigned int ftrace_read_cnt; + unsigned int device_info_read_cnt; unsigned int pmsg_read_cnt; struct pstore_info pstore; }; @@ -118,6 +126,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) cxt->dump_read_cnt = 0; cxt->console_read_cnt = 0; cxt->ftrace_read_cnt = 0; + cxt->device_info_read_cnt = 0; cxt->pmsg_read_cnt = 0; return 0; } @@ -217,14 +226,21 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, } if (!prz_ok(prz)) - prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, - 1, id, type, PSTORE_TYPE_CONSOLE, 0); + prz = ramoops_get_next_prz(&cxt->cprz, + &cxt->console_read_cnt, + 1, id, type, PSTORE_TYPE_CONSOLE, 0); if (!prz_ok(prz)) - prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, - 1, id, type, PSTORE_TYPE_FTRACE, 0); + prz = ramoops_get_next_prz(&cxt->fprz, + &cxt->ftrace_read_cnt, + 1, id, type, PSTORE_TYPE_FTRACE, 0); if (!prz_ok(prz)) - prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, - 1, id, type, PSTORE_TYPE_PMSG, 0); + prz = ramoops_get_next_prz(&cxt->dprz, + &cxt->device_info_read_cnt, + 1, id, type, PSTORE_TYPE_DEVICE_INFO, 0); + if (!prz_ok(prz)) + prz = ramoops_get_next_prz(&cxt->mprz, + &cxt->pmsg_read_cnt, + 1, id, type, PSTORE_TYPE_PMSG, 0); if (!prz_ok(prz)) return 0; @@ -287,6 +303,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, return -ENOMEM; persistent_ram_write(cxt->fprz, buf, size); return 0; + } else if (type == PSTORE_TYPE_DEVICE_INFO) { + if (!cxt->dprz) + return -ENOMEM; + persistent_ram_write(cxt->dprz, buf, size); + return 0; } else if (type == PSTORE_TYPE_PMSG) { if (!cxt->mprz) return -ENOMEM; @@ -367,6 +388,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, case PSTORE_TYPE_FTRACE: prz = cxt->fprz; break; + case PSTORE_TYPE_DEVICE_INFO: + prz = cxt->dprz; + break; case PSTORE_TYPE_PMSG: prz = cxt->mprz; break; @@ -494,30 +518,6 @@ void notrace ramoops_console_write_buf(const char *buf, size_t size) persistent_ram_write(cxt->cprz, buf, size); } -static int ramoops_parse_dt_size(struct platform_device *pdev, - const char *propname, unsigned long *val) -{ - u64 val64; - int ret; - - ret = of_property_read_u64(pdev->dev.of_node, propname, &val64); - if (ret == -EINVAL) { - *val = 0; - return 0; - } else if (ret != 0) { - dev_err(&pdev->dev, "failed to parse property %s: %d\n", - propname, ret); - return ret; - } - - if (val64 > ULONG_MAX) { - dev_err(&pdev->dev, "invalid %s %llu\n", propname, val64); - return -EOVERFLOW; - } - - *val = val64; - return 0; -} static int ramoops_parse_dt(struct platform_device *pdev, struct ramoops_platform_data *pdata) @@ -549,32 +549,13 @@ static int ramoops_parse_dt(struct platform_device *pdev, pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); - ret = ramoops_parse_dt_size(pdev, "record-size", &pdata->record_size); - if (ret < 0) - return ret; - - ret = ramoops_parse_dt_size(pdev, "console-size", &pdata->console_size); - if (ret < 0) - return ret; - - ret = ramoops_parse_dt_size(pdev, "ftrace-size", &pdata->ftrace_size); - if (ret < 0) - return ret; - - ret = ramoops_parse_dt_size(pdev, "pmsg-size", &pdata->pmsg_size); - if (ret < 0) - return ret; - - ret = of_property_read_u32(of_node, "ecc-size", &ecc_size); - if (ret == 0) { - if (ecc_size > INT_MAX) { - dev_err(&pdev->dev, "invalid ecc-size %u\n", ecc_size); - return -EOVERFLOW; - } - pdata->ecc_info.ecc_size = ecc_size; - } else if (ret != -EINVAL) { - return ret; - } + pdata->record_size = record_size; + pdata->console_size = ramoops_console_size; + pdata->ftrace_size = ramoops_ftrace_size; + pdata->device_info_size = ramoops_device_info_size; + pdata->pmsg_size = ramoops_pmsg_size; + pdata->dump_oops = dump_oops; + pdata->ecc_info.ecc_size = ecc_size = 0; return 0; } @@ -606,8 +587,11 @@ static int ramoops_probe(struct platform_device *pdev) if (cxt->max_dump_cnt) goto fail_out; - if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && - !pdata->ftrace_size && !pdata->pmsg_size)) { + if (!pdata->mem_size || (!pdata->record_size + && !pdata->console_size + && !pdata->ftrace_size + && !pdata->device_info_size + && !pdata->pmsg_size)) { pr_err("The memory size and the record/console size must be " "non-zero\n"); goto fail_out; @@ -619,6 +603,9 @@ static int ramoops_probe(struct platform_device *pdev) pdata->console_size = rounddown_pow_of_two(pdata->console_size); if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); + if (pdata->device_info_size && !is_power_of_2(pdata->device_info_size)) + pdata->device_info_size + = rounddown_pow_of_two(pdata->device_info_size); if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); @@ -628,14 +615,18 @@ static int ramoops_probe(struct platform_device *pdev) cxt->record_size = pdata->record_size; cxt->console_size = pdata->console_size; cxt->ftrace_size = pdata->ftrace_size; + cxt->device_info_size = pdata->device_info_size; cxt->pmsg_size = pdata->pmsg_size; cxt->dump_oops = pdata->dump_oops; cxt->ecc_info = pdata->ecc_info; paddr = cxt->phys_addr; - dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - - cxt->pmsg_size; + dump_mem_sz = cxt->size + - cxt->console_size + - cxt->ftrace_size + - cxt->device_info_size + - cxt->pmsg_size; err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); if (err) goto fail_out; @@ -654,6 +645,16 @@ static int ramoops_probe(struct platform_device *pdev) if (err) goto fail_init_mprz; + /*paddr = pdata->mem_address + * + pdata->mem_size - cxt->device_info_size; + */ + err = ramoops_init_prz(dev, cxt, &cxt->dprz, &paddr, + cxt->device_info_size, 0); + if (err) + goto fail_init_dprz; + + memset(cxt->dprz->buffer->data, 0, cxt->dprz->buffer_size); + cxt->pstore.data = cxt; /* * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we @@ -689,6 +690,7 @@ static int ramoops_probe(struct platform_device *pdev) ramoops_console_size = pdata->console_size; ramoops_pmsg_size = pdata->pmsg_size; ramoops_ftrace_size = pdata->ftrace_size; + ramoops_device_info_size = pdata->device_info_size; pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -700,6 +702,8 @@ fail_buf: kfree(cxt->pstore.buf); fail_clear: cxt->pstore.bufsize = 0; + kfree(cxt->dprz); +fail_init_dprz: kfree(cxt->mprz); fail_init_mprz: kfree(cxt->fprz); @@ -716,12 +720,14 @@ static int ramoops_remove(struct platform_device *pdev) struct ramoops_context *cxt = &oops_cxt; pstore_unregister(&cxt->pstore); + cxt->max_dump_cnt = 0; kfree(cxt->pstore.buf); cxt->pstore.bufsize = 0; persistent_ram_free(cxt->mprz); persistent_ram_free(cxt->fprz); + persistent_ram_free(cxt->dprz); persistent_ram_free(cxt->cprz); ramoops_free_przs(cxt); @@ -761,6 +767,7 @@ static void ramoops_register_dummy(void) dummy_data->record_size = record_size; dummy_data->console_size = ramoops_console_size; dummy_data->ftrace_size = ramoops_ftrace_size; + dummy_data->device_info_size = ramoops_device_info_size; dummy_data->pmsg_size = ramoops_pmsg_size; dummy_data->dump_oops = dump_oops; /* diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index e11672aa4575..a573ca2afa78 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -29,12 +29,7 @@ #include #include -struct persistent_ram_buffer { - uint32_t sig; - atomic_t start; - atomic_t size; - uint8_t data[0]; -}; + #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1461254f301d..b910affb03d3 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -241,7 +241,11 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data); + if (!saved_cred) { + err = -ENOMEM; + goto out_err; + } file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); @@ -271,7 +275,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); out_revert_cred: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: dput(parent); return err; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b41eb7fa37bb..6d4065784699 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,7 +22,6 @@ #include #include -/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) { @@ -50,7 +49,6 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, return old_cred; } -/* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred) { const struct cred *cur_cred; @@ -78,7 +76,10 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -95,8 +96,11 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, err = -ENOMEM; goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -110,58 +114,18 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fixup_lower_ownership(dentry, dentry->d_name.name); out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); free_fs_struct(copied_fs); out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_dir_dentry; - u64 file_size_save; - int err; - struct path lower_old_path, lower_new_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - file_size_save = i_size_read(d_inode(old_dentry)); - sdcardfs_get_lower_path(old_dentry, &lower_old_path); - sdcardfs_get_lower_path(new_dentry, &lower_new_path); - lower_old_dentry = lower_old_path.dentry; - lower_new_dentry = lower_new_path.dentry; - lower_dir_dentry = lock_parent(lower_new_dentry); - - err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), - lower_new_dentry, NULL); - if (err || !d_inode(lower_new_dentry)) - goto out; - - err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); - fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); - set_nlink(d_inode(old_dentry), - sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); - i_size_write(d_inode(new_dentry), file_size_save); -out: - unlock_dir(lower_dir_dentry); - sdcardfs_put_lower_path(old_dentry, &lower_old_path); - sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(); - return err; -} -#endif - static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; @@ -178,7 +142,10 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -209,43 +176,11 @@ out: unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); - if (err) - goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - static int touch(char *abs_path, mode_t mode) { struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); @@ -287,7 +222,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* check disk space */ parent_dentry = dget_parent(dentry); @@ -316,8 +254,11 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode unlock_dir(lower_parent_dentry); goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); if (err) { @@ -366,23 +307,34 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if (make_nomedia_in_obb || ((pd->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { - REVERT_CRED(saved_cred); - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); + revert_fsids(saved_cred); + saved_cred = override_fsids(sbi, + SDCARDFS_I(dentry->d_inode)->data); + if (!saved_cred) { + pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, + -ENOMEM); + goto out; + } set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", - lower_path.dentry->d_name.name, touch_err); + lower_path.dentry->d_name.name, + touch_err); goto out; } } out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); + free_fs_struct(copied_fs); out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -402,7 +354,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. @@ -427,44 +382,11 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) out: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); - if (err) - goto out; - - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - /* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. @@ -489,7 +411,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); + saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb), + SDCARDFS_I(new_dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -536,7 +461,7 @@ out: dput(lower_new_dir_dentry); sdcardfs_put_real_lower(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -655,33 +580,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); - /* XXX - * Original sdcardfs code calls inode_permission(lower_inode,.. ) - * for checking inode permission. But doing such things here seems - * duplicated work, because the functions called after this func, - * such as vfs_create, vfs_unlink, vfs_rename, and etc, - * does exactly same thing, i.e., they calls inode_permission(). - * So we just let they do the things. - * If there are any security hole, just uncomment following if block. - */ -#if 0 - if (!err) { - /* - * Permission check on lower_inode(=EXT4). - * we check it with AID_MEDIA_RW permission - */ - struct inode *lower_inode; - - OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); - - lower_inode = sdcardfs_lower_inode(inode); - err = inode_permission(lower_inode, mask); - - REVERT_CRED(); - } -#endif return err; - } static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) @@ -756,7 +655,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct goto out_err; /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb), + SDCARDFS_I(inode)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -815,7 +717,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct out: sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: return err; } @@ -898,13 +800,6 @@ const struct inode_operations sdcardfs_dir_iops = { .setattr = sdcardfs_setattr_wrn, .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, - /* XXX Following operations are implemented, - * but FUSE(sdcard) or FAT does not support them - * These methods are *NOT* perfectly tested. - .symlink = sdcardfs_symlink, - .link = sdcardfs_link, - .mknod = sdcardfs_mknod, - */ }; const struct inode_operations sdcardfs_main_iops = { diff --git a/fs/timerfd.c b/fs/timerfd.c index 0548c572839c..1327a02ec778 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -50,8 +50,7 @@ static DEFINE_SPINLOCK(cancel_lock); static inline bool isalarm(struct timerfd_ctx *ctx) { return ctx->clockid == CLOCK_REALTIME_ALARM || - ctx->clockid == CLOCK_BOOTTIME_ALARM || - ctx->clockid == CLOCK_POWEROFF_ALARM; + ctx->clockid == CLOCK_BOOTTIME_ALARM; } /* @@ -143,8 +142,7 @@ static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) { spin_lock(&ctx->cancel_lock); if ((ctx->clockid == CLOCK_REALTIME || - ctx->clockid == CLOCK_REALTIME_ALARM || - ctx->clockid == CLOCK_POWEROFF_ALARM) && + ctx->clockid == CLOCK_REALTIME_ALARM) && (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { if (!ctx->might_cancel) { ctx->might_cancel = true; @@ -176,7 +174,6 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags, enum hrtimer_mode htmode; ktime_t texp; int clockid = ctx->clockid; - enum alarmtimer_type type; htmode = (flags & TFD_TIMER_ABSTIME) ? HRTIMER_MODE_ABS: HRTIMER_MODE_REL; @@ -187,8 +184,10 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags, ctx->tintv = timespec_to_ktime(ktmr->it_interval); if (isalarm(ctx)) { - type = clock2alarm(ctx->clockid); - alarm_init(&ctx->t.alarm, type, timerfd_alarmproc); + alarm_init(&ctx->t.alarm, + ctx->clockid == CLOCK_REALTIME_ALARM ? + ALARM_REALTIME : ALARM_BOOTTIME, + timerfd_alarmproc); } else { hrtimer_init(&ctx->t.tmr, clockid, htmode); hrtimer_set_expires(&ctx->t.tmr, texp); @@ -388,7 +387,6 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) { int ufd; struct timerfd_ctx *ctx; - enum alarmtimer_type type; /* Check the TFD_* constants for consistency. */ BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC); @@ -399,8 +397,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) clockid != CLOCK_REALTIME && clockid != CLOCK_REALTIME_ALARM && clockid != CLOCK_BOOTTIME && - clockid != CLOCK_BOOTTIME_ALARM && - clockid != CLOCK_POWEROFF_ALARM)) + clockid != CLOCK_BOOTTIME_ALARM)) return -EINVAL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -411,12 +408,13 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) spin_lock_init(&ctx->cancel_lock); ctx->clockid = clockid; - if (isalarm(ctx)) { - type = clock2alarm(ctx->clockid); - alarm_init(&ctx->t.alarm, type, timerfd_alarmproc); - } else { + if (isalarm(ctx)) + alarm_init(&ctx->t.alarm, + ctx->clockid == CLOCK_REALTIME_ALARM ? + ALARM_REALTIME : ALARM_BOOTTIME, + timerfd_alarmproc); + else hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS); - } ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 }); @@ -488,10 +486,6 @@ static int do_timerfd_settime(int ufd, int flags, ret = timerfd_setup(ctx, flags, new); spin_unlock_irq(&ctx->wqh.lock); - - if (ctx->clockid == CLOCK_POWEROFF_ALARM) - set_power_on_alarm(); - fdput(f); return ret; } diff --git a/include/dt-bindings/clock/msm-clocks-8998.h b/include/dt-bindings/clock/msm-clocks-8998.h index 67e47c46e09a..b4059bf83cbd 100644 --- a/include/dt-bindings/clock/msm-clocks-8998.h +++ b/include/dt-bindings/clock/msm-clocks-8998.h @@ -25,6 +25,7 @@ #define clk_cnoc_a_clk 0xd8fe2ccc #define clk_snoc_clk 0x2c341aa0 #define clk_snoc_a_clk 0x8fcef2af +#define clk_uart_cnoc_msmbus_a_clk 0x799be48c #define clk_cnoc_periph_clk 0xb11e9cf9 #define clk_cnoc_periph_a_clk 0x1d7faa2e #define clk_cnoc_periph_keepalive_a_clk 0x7287aef2 @@ -82,6 +83,7 @@ #define clk_scm_ce1_clk 0xd8ebcc62 #define clk_snoc_msmbus_clk 0xe6900bb6 #define clk_snoc_msmbus_a_clk 0x5d4683bd +#define clk_uart_snoc_msmbus_a_clk 0xe623f321 #define clk_gcc_ce1_ahb_m_clk 0x2eb28c01 #define clk_gcc_ce1_axi_m_clk 0xc174dfba #define clk_aggre1_noc_clk 0x049abba8 diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 38a75b5d4792..9333ae59c8c8 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -5,12 +5,10 @@ #include #include #include -#include enum alarmtimer_type { ALARM_REALTIME, ALARM_BOOTTIME, - ALARM_POWEROFF_REALTIME, ALARM_NUMTYPE, }; @@ -50,9 +48,6 @@ void alarm_start_relative(struct alarm *alarm, ktime_t start); void alarm_restart(struct alarm *alarm); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); -void set_power_on_alarm(void); -void power_on_alarm_init(void); -enum alarmtimer_type clock2alarm(clockid_t clockid); u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); u64 alarm_forward_now(struct alarm *alarm, ktime_t interval); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index a998cf205cdc..e5c512729bfb 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -154,6 +154,10 @@ struct cpufreq_policy { struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu); struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); void cpufreq_cpu_put(struct cpufreq_policy *policy); +extern unsigned int cluster1_first_cpu; +extern bool fp_irq_cnt; +extern void c0_cpufreq_limit_queue(void); +extern void c1_cpufreq_limit_queue(void); #else static inline struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) { diff --git a/include/linux/freezer.h b/include/linux/freezer.h index dd03e837ebb7..f17026db09c5 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -66,6 +66,10 @@ static inline bool try_to_freeze(void) return try_to_freeze_unsafe(); } +/*huruihuan add for freezing task in cgroup despite of PF_FREEZER_SKIP flag*/ +extern void unfreezer_fork(struct task_struct *task); +extern bool freeze_cgroup_task(struct task_struct *p); + extern bool freeze_task(struct task_struct *p); extern bool set_freezable(void); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index d1baebf350d8..77cfe8d0d6ab 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -106,11 +106,17 @@ struct gpio_chip { unsigned offset, int value); int (*get)(struct gpio_chip *chip, unsigned offset); +/*2017-08-22 add for dash adapter update*/ + int (*get_dash)(struct gpio_chip *chip, + unsigned offset); void (*set)(struct gpio_chip *chip, unsigned offset, int value); void (*set_multiple)(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits); +/*2017-08-22 add for dash adapter update*/ + void (*set_dash)(struct gpio_chip *chip, + unsigned offset, int value); int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce); diff --git a/include/linux/input/qpnp-power-on.h b/include/linux/input/qpnp-power-on.h index 5944f0fd3414..9d75c190e66a 100644 --- a/include/linux/input/qpnp-power-on.h +++ b/include/linux/input/qpnp-power-on.h @@ -50,6 +50,37 @@ enum pon_power_off_type { PON_POWER_OFF_MAX_TYPE = 0x10, }; +struct qpnp_pon { + struct platform_device *pdev; + struct regmap *regmap; + struct input_dev *pon_input; + struct qpnp_pon_config *pon_cfg; + struct pon_regulator *pon_reg_cfg; + struct list_head list; + struct delayed_work bark_work; + struct delayed_work press_work; + struct work_struct up_work; + atomic_t press_count; + struct dentry *debugfs; + int pon_trigger_reason; + int pon_power_off_reason; + int num_pon_reg; + int num_pon_config; + u32 dbc_time_us; + u32 uvlo; + int warm_reset_poff_type; + int hard_reset_poff_type; + int shutdown_poff_type; + u16 base; + u8 subtype; + u8 pon_ver; + u8 warm_reset_reason1; + u8 warm_reset_reason2; + bool is_spon; + bool store_hard_reset_reason; + bool kpdpwr_dbc_enable; + ktime_t kpdpwr_last_release_time; +}; enum pon_restart_reason { /* 0 ~ 31 for common defined features */ PON_RESTART_REASON_UNKNOWN = 0x00, @@ -59,12 +90,34 @@ enum pon_restart_reason { PON_RESTART_REASON_DMVERITY_CORRUPTED = 0x04, PON_RESTART_REASON_DMVERITY_ENFORCE = 0x05, PON_RESTART_REASON_KEYS_CLEAR = 0x06, + PON_RESTART_REASON_AGING = 0x07, + PON_RESTART_REASON_REBOOT = 0x10, + PON_RESTART_REASON_FACTORY = 0x11, + PON_RESTART_REASON_WLAN = 0x12, + PON_RESTART_REASON_RF = 0x13, + PON_RESTART_REASON_MOS = 0x14, + PON_RESTART_REASON_KERNEL = 0x15, + PON_RESTART_REASON_ANDROID = 0x16, + PON_RESTART_REASON_MODEM = 0x17, + PON_RESTART_REASON_PANIC = 0x18, /* 32 ~ 63 for OEMs/ODMs secific features */ PON_RESTART_REASON_OEM_MIN = 0x20, PON_RESTART_REASON_OEM_MAX = 0x3f, }; +/* Define OEM reboot mode magic*/ +#define AGING_MODE 0x77665510 +#define FACTORY_MODE 0x77665504 +#define WLAN_MODE 0x77665505 +#define RF_MODE 0x77665506 +#define MOS_MODE 0x77665507 +#define KERNEL_MODE 0x7766550d +#define ANDROID_MODE 0x7766550c +#define MODEM_MODE 0x7766550b +#define OEM_PANIC 0x77665518 + + #ifdef CONFIG_INPUT_QPNP_POWER_ON int qpnp_pon_system_pwr_off(enum pon_power_off_type type); int qpnp_pon_is_warm_reset(void); diff --git a/include/linux/oem_force_dump.h b/include/linux/oem_force_dump.h new file mode 100644 index 000000000000..476fcc1f2495 --- /dev/null +++ b/include/linux/oem_force_dump.h @@ -0,0 +1,17 @@ +/* + * oem_force_dump.h + * + * header file supporting debug functions for Oneplus device. + * + */ + +#ifndef OEM_FORCE_DUMP_H +#define OEM_FORCE_DUMP_H + +extern void oem_check_force_dump_key(unsigned int code, int value); +extern int oem_get_download_mode(void); +void send_msg(char *message); +int msm_serial_oem_init(void); +extern int selinux_switch; + +#endif diff --git a/include/linux/oneplus/boot_mode.h b/include/linux/oneplus/boot_mode.h new file mode 100644 index 000000000000..f595f407cb04 --- /dev/null +++ b/include/linux/oneplus/boot_mode.h @@ -0,0 +1,16 @@ +#ifndef _BOOT_MODE_H_ +#define _BOOT_MODE_H_ 1 + +enum oem_boot_mode { + MSM_BOOT_MODE__NORMAL, + MSM_BOOT_MODE__FASTBOOT, + MSM_BOOT_MODE__RECOVERY, + MSM_BOOT_MODE__AGING, + MSM_BOOT_MODE__FACTORY, + MSM_BOOT_MODE__RF, + MSM_BOOT_MODE__WLAN, + MSM_BOOT_MODE__MOS, + MSM_BOOT_MODE__CHARGE, +}; +enum oem_boot_mode get_boot_mode(void); +#endif diff --git a/include/linux/op_rf_cable_monitor.h b/include/linux/op_rf_cable_monitor.h new file mode 100644 index 000000000000..9a43dded248c --- /dev/null +++ b/include/linux/op_rf_cable_monitor.h @@ -0,0 +1,10 @@ +#ifndef _OP_RF_CABLE_MONITOR +#define _OP_RF_CABLE_MONITOR 1 + +extern char *saved_command_line; +extern void op_restart_modem(void); + +#define CABLE_WAKELOCK_HOLD_TIME 5000 +#define PAGESIZE 512 + +#endif diff --git a/include/linux/param_rw.h b/include/linux/param_rw.h new file mode 100644 index 000000000000..cb3978255fca --- /dev/null +++ b/include/linux/param_rw.h @@ -0,0 +1,36 @@ +#ifndef __PARAM_RW_H +#define __PARAM_RW_H + +#define PARAM_SID_LENGTH 1024 +#define DEFAULT_PARAM_DUMP_SIZE 64 + +typedef unsigned int uint32; + +typedef enum { + PARAM_SID_PRODUCT = 0, + PARAM_SID_CONFIG, + PARAM_SID_LCD, + PARAM_SID_TP, + PARAM_SID_TP_KPD, + PARAM_SID_CAMERA, + PARAM_SID_SENSORS, + PARAM_SID_BATTERY, + PARAM_SID_RTC, + PARAM_SID_CRASH_RECORD, + PARAM_SID_SALEINFO, + PARAM_SID_MISC, + PARAM_SID_DOWNLOAD, + PARAM_SID_PHONE_HISTORY, + NUM_PARAM_PLAINTEXT_SEGMENT, + + PARAM_SID_INVALID = -1 +} param_sid_index_t; + +int get_param_by_index_and_offset(uint32 sid_index, uint32 offset, void * buf, int length); +int set_param_by_index_and_offset(uint32 sid_index, uint32 offset, void * buf, int length); + +int add_restart_08_count(void); +int add_restart_other_count(void); +//end +#endif + diff --git a/include/linux/platform_data/pn544.h b/include/linux/platform_data/pn544.h deleted file mode 100644 index 5ce1ab983f44..000000000000 --- a/include/linux/platform_data/pn544.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Driver include for the PN544 NFC chip. - * - * Copyright (C) Nokia Corporation - * - * Author: Jari Vanhala - * Contact: Matti Aaltoenn - * - * 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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _PN544_H_ -#define _PN544_H_ - -#include - -enum { - NFC_GPIO_ENABLE, - NFC_GPIO_FW_RESET, - NFC_GPIO_IRQ -}; - -/* board config */ -struct pn544_nfc_platform_data { - int (*request_resources) (struct i2c_client *client); - void (*free_resources) (void); - void (*enable) (int fw); - int (*test) (void); - void (*disable) (void); - int (*get_gpio)(int type); -}; - -#endif /* _PN544_H_ */ diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index ff49ab3516b1..b00ed60e9554 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -18,6 +18,12 @@ enum { PM_QOS_NETWORK_LATENCY, PM_QOS_NETWORK_THROUGHPUT, PM_QOS_MEMORY_BANDWIDTH, + PM_QOS_C0_CPUFREQ_MAX, + PM_QOS_C0_CPUFREQ_MIN, + PM_QOS_C1_CPUFREQ_MAX, + PM_QOS_C1_CPUFREQ_MIN, + PM_QOS_DEVFREQ_MAX, + PM_QOS_DEVFREQ_MIN, /* insert new class ID */ PM_QOS_NUM_CLASSES, @@ -40,6 +46,16 @@ enum pm_qos_flags_status { #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) #define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1)) +#define MIN_CPUFREQ 0 +#define MAX_CPUFREQ 0x40 +#define MASK_CPUFREQ 0xE0 + +#define PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE MAX_CPUFREQ +#define PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE MIN_CPUFREQ +#define PM_QOS_DEVFREQ_MAX_DEFAULT_VALUE MAX_CPUFREQ +#define PM_QOS_DEVFREQ_MIN_DEFAULT_VALUE MIN_CPUFREQ + +extern void msm_cpuidle_set_sleep_disable(bool disable); #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) #define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index a3447932df1f..6ddd84e3ad33 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -108,7 +108,7 @@ extern void __pm_relax(struct wakeup_source *ws); extern void pm_relax(struct device *dev); extern void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec); extern void pm_wakeup_event(struct device *dev, unsigned int msec); - +extern void pm_print_active_wakeup_sources_queue(bool on); #else /* !CONFIG_PM_SLEEP */ static inline void device_set_wakeup_capable(struct device *dev, bool capable) diff --git a/include/linux/power/oem_external_fg.h b/include/linux/power/oem_external_fg.h new file mode 100644 index 000000000000..775e3e36f2d7 --- /dev/null +++ b/include/linux/power/oem_external_fg.h @@ -0,0 +1,118 @@ +#ifndef __OEM_EXTERNAL_FG_H__ +#define __OEM_EXTERNAL_FG_H__ +#include +#include +enum { + ADAPTER_FW_UPDATE_NONE, + ADAPTER_FW_NEED_UPDATE, + ADAPTER_FW_UPDATE_SUCCESS, + ADAPTER_FW_UPDATE_FAIL, +}; +struct op_adapter_chip { + int timer_delay; + bool tx_byte_over; + bool rx_byte_over; + bool rx_timeout; + unsigned long uart_tx_gpio; + unsigned long uart_rx_gpio; + char *adapter_firmware_data; + unsigned int adapter_fw_data_count; + unsigned int tx_invalid_val; + bool adapter_update_ing; + struct op_adapter_operations *vops; +}; +struct op_adapter_operations { + bool (*adapter_update)(struct op_adapter_chip *chip, + unsigned long tx_pin, unsigned long rx_pin); +}; + +struct external_battery_gauge { + int (*get_battery_mvolts)(void); + int (*get_battery_temperature)(void); + bool (*is_battery_present)(void); + bool (*is_battery_temp_within_range)(void); + bool (*is_battery_id_valid)(void); + bool (*is_usb_switch_on)(void); + int (*get_battery_status)(void); + int (*get_batt_remaining_capacity)(void); + int (*get_batt_health)(void); + int (*get_batt_bq_soc)(void); + int (*monitor_for_recharging)(void); + int (*get_battery_soc)(void); + int (*get_average_current)(void); + int (*get_batt_cc)(void); + int (*get_batt_fcc)(void); + bool (*fast_chg_started)(void); + bool (*fast_switch_to_normal)(void); + int (*set_switch_to_noraml_false)(void); + int (*set_fast_chg_allow)(bool enable); + bool (*get_fast_chg_allow)(void); + int (*fast_normal_to_warm)(void); + int (*set_normal_to_warm_false)(void); + int (*get_adapter_update)(void); + bool (*get_fast_chg_ing)(void); + bool (*get_fast_low_temp_full)(void); + int (*set_low_temp_full_false)(void); + int (*set_allow_reading)(int enable); + int (*set_lcd_off_status)(int status); + int (*fast_chg_started_status)(bool status); + bool (*get_fastchg_firmware_already_updated)(void); + int (*get_device_type)(void); +}; + +struct notify_dash_event { + int (*notify_event)(void); + int (*op_contrl)(int status, bool check_power_ok); + int (*notify_dash_charger_present)(int true); +}; + +struct notify_usb_enumeration_status { + int (*notify_usb_enumeration)(int status); +}; + +enum temp_region_type { + BATT_TEMP_COLD = 0, + BATT_TEMP_LITTLE_COLD, + BATT_TEMP_COOL, + BATT_TEMP_LITTLE_COOL, + BATT_TEMP_PRE_NORMAL, + BATT_TEMP_NORMAL, + BATT_TEMP_WARM, + BATT_TEMP_HOT, + BATT_TEMP_INVALID, +}; + +enum batt_status_type { + BATT_STATUS_GOOD, + BATT_STATUS_BAD_TEMP, /* cold or hot */ + BATT_STATUS_BAD, + BATT_STATUS_REMOVED, /* on v2.2 only */ + BATT_STATUS_INVALID_v1 = BATT_STATUS_REMOVED, + BATT_STATUS_INVALID +}; +void op_pm8998_regmap_register(struct qpnp_pon *pon); + +void regsister_notify_usb_enumeration_status( + struct notify_usb_enumeration_status *event); +void notify_dash_unplug_register(struct notify_dash_event *event); +void notify_dash_unplug_unregister(struct notify_dash_event *event); +void fastcharge_information_unregister(struct external_battery_gauge *fast_chg); +void fastcharge_information_register(struct external_battery_gauge *fast_chg); +void external_battery_gauge_register(struct external_battery_gauge *batt_gauge); +void external_battery_gauge_unregister( + struct external_battery_gauge *batt_gauge); +void bq27541_information_register(struct external_battery_gauge *fast_chg); +void bq27541_information_unregister(struct external_battery_gauge *fast_chg); +bool get_extern_fg_regist_done(void); +bool get_extern_bq_present(void); +int get_prop_pre_shutdown_soc(void); +extern int get_charging_status(void); +extern int fuelgauge_battery_temp_region_get(void); +extern bool get_oem_charge_done_status(void); +extern int load_soc(void); +extern void backup_soc_ex(int soc); +extern void clean_backup_soc_ex(void); +/*add for dash adapter update*/ +extern bool dash_adapter_update_is_tx_gpio(unsigned long gpio_num); +extern bool dash_adapter_update_is_rx_gpio(unsigned long gpio_num); +#endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 6828063842df..6d5c9561f033 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -118,6 +118,23 @@ enum { enum power_supply_property { /* Properties of type `int' */ POWER_SUPPLY_PROP_STATUS = 0, + POWER_SUPPLY_PROP_SET_ALLOW_READ_EXTERN_FG_IIC, + POWER_SUPPLY_PROP_CC_TO_CV_POINT, + POWER_SUPPLY_PROP_CHG_PROTECT_STATUS, + POWER_SUPPLY_PROP_FASTCHG_STATUS, + POWER_SUPPLY_PROP_FASTCHG_STARTING, + POWER_SUPPLY_CUTOFF_VOLT_WITH_CHARGER, + POWER_SUPPLY_PROP_UPDATE_LCD_IS_OFF, + POWER_SUPPLY_PROP_CHECK_USB_UNPLUG, + POWER_SUPPLY_PROP_OTG_SWITCH, + POWER_SUPPLY_PROP_SWITCH_DASH, + POWER_SUPPLY_PROP_NOTIFY_CHARGER_SET_PARAMETER, + POWER_SUPPLY_PROP_FG_CAPACITY, + POWER_SUPPLY_PROP_FG_VOLTAGE_NOW, + POWER_SUPPLY_PROP_FG_CURRENT_NOW, + POWER_SUPPLY_PROP_IS_AGING_TEST, + POWER_SUPPLY_PROP_BATTERY_HEALTH, + POWER_SUPPLY_PROP_BQ_SOC, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, @@ -232,6 +249,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CURRENT_CAPABILITY, POWER_SUPPLY_PROP_TYPEC_MODE, POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, /* 0: N/C, 1: CC1, 2: CC2 */ +/* xianglin add */ + POWER_SUPPLY_PROP_OEM_TYPEC_CC_ORIENTATION, /* 0: N/C, 1: CC1, 2: CC2 */ POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, @@ -291,6 +310,7 @@ enum power_supply_type { POWER_SUPPLY_TYPE_TYPEC, /* Type-C */ POWER_SUPPLY_TYPE_UFP, /* Type-C UFP */ POWER_SUPPLY_TYPE_DFP, /* TYpe-C DFP */ + POWER_SUPPLY_TYPE_DASH, }; /* Indicates USB Type-C CC connection status */ diff --git a/include/linux/printk.h b/include/linux/printk.h index 9729565c25ff..7ba36111014f 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -468,5 +468,5 @@ static inline void print_hex_dump_debug(const char *prefix_str, int prefix_type, { } #endif - +int force_oem_console_setup(char *str); #endif diff --git a/include/linux/project_info.h b/include/linux/project_info.h new file mode 100644 index 000000000000..5ed67d1c3621 --- /dev/null +++ b/include/linux/project_info.h @@ -0,0 +1,77 @@ +#ifndef _PROJECT_INFO_H_ +#define _PROJECT_INFO_H_ 1 +typedef __u32 uint32; +typedef __u8 uint8; + +/*******SECURE_BOOTn = 0x00786078+ 0x4*n, n=[1..14]******/ +#define SECURE_BOOT_BASE 0x00786078 +#define SECURE_BOOT1 (SECURE_BOOT_BASE + 0x4*1) +#define BUF_SIZE 64 + +extern uint32_t chip_serial_num; +extern unsigned long totalram_pages __read_mostly; + + +struct project_info { + char project_name[8]; + uint32 hw_version; + uint32 rf_v1; + uint32 rf_v2; + uint32 rf_v3; + uint32 modem; + uint32 operator; + uint32 ddr_manufacture_info; + uint32 ddr_row; + uint32 ddr_column; + uint32 ddr_fw_version; + uint32 ddr_reserve_info; + uint32 platform_id; + uint32 ftm_uart_boot_mode; + uint32 feature_id; +}; + +struct component_info { + char *version; + char *manufacture; +}; + +enum { + HW_VERSION__UNKNOWN, + HW_VERSION__11 = 11, + HW_VERSION__12, +}; + +enum COMPONENT_TYPE { + DDR, + EMMC, + F_CAMERA, + R_CAMERA, + SECOND_R_CAMERA, + TP, + LCD, + WCN, + I_SENSOR, + G_SENSOR, + M_SENSOR, + GYRO, + BACKLIGHT, + MAINBOARD, + /*Add new component here*/ + FINGERPRINTS, + TOUCH_KEY, + UFS, + ABOARD, + NFC, + FAST_CHARGE, + CPU, + COMPONENT_MAX, +}; + + +int push_component_info(enum COMPONENT_TYPE type, + char *version, char *manufacture); +int reset_component_info(enum COMPONENT_TYPE type); +uint32 get_hw_version(void); + + +#endif diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 5cae2c6c90ad..b62a8cf2385e 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -42,6 +42,7 @@ enum pstore_type_id { PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PMSG = 7, PSTORE_TYPE_PPC_OPAL = 8, + PSTORE_TYPE_DEVICE_INFO = 13, PSTORE_TYPE_UNKNOWN = 255 }; diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 7097a45dbc25..991bd8943e77 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -31,9 +31,16 @@ */ #define PRZ_FLAG_NO_LOCK BIT(0) -struct persistent_ram_buffer; +/*struct persistent_ram_buffer;*/ struct rs_control; +struct persistent_ram_buffer { + uint32_t sig; + atomic_t start; + atomic_t size; + uint8_t data[0]; +}; + struct persistent_ram_ecc_info { int block_size; int ecc_size; @@ -95,6 +102,7 @@ struct ramoops_platform_data { unsigned long record_size; unsigned long console_size; unsigned long ftrace_size; + unsigned long device_info_size; unsigned long pmsg_size; int dump_oops; struct persistent_ram_ecc_info ecc_info; diff --git a/include/linux/sched.h b/include/linux/sched.h index 5fe8c3dfd60f..8ea2ea1061ef 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1648,6 +1648,12 @@ struct task_struct { atomic_t usage; unsigned int flags; /* per process flags, defined below */ unsigned int ptrace; + //huruihuan add for kill task in D status + unsigned int kill_flag; + struct timespec ttu; + + /* add for fd leak debug */ + bool dump_fd_leak; #ifdef CONFIG_SMP struct llist_node wake_entry; diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h index 03835522dfcb..54d41acb6974 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -53,6 +53,8 @@ static inline gid_t __kgid_val(kgid_t gid) #define GLOBAL_ROOT_UID KUIDT_INIT(0) #define GLOBAL_ROOT_GID KGIDT_INIT(0) +//huruihuan add for cgroup control +#define GLOBAL_SYSTEM_UID KUIDT_INIT(1000) #define INVALID_UID KUIDT_INIT(-1) #define INVALID_GID KGIDT_INIT(-1) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0e61b1f65359..3460cccc09c9 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -980,6 +980,7 @@ static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) { if (!gadget->ops->vbus_draw) return -EOPNOTSUPP; + pr_info("%s USB setting current is %umA\n", __func__, mA); return gadget->ops->vbus_draw(gadget, mA); } diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index a9fbf1b38e71..1119801cc6a2 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -71,6 +71,8 @@ #define WRITE_SAME 0x41 #define UNMAP 0x42 #define READ_TOC 0x43 +/*2016/09/21, CD-ROM and VID customized*/ +#define READ_CD 0xbe #define READ_HEADER 0x44 #define GET_EVENT_STATUS_NOTIFICATION 0x4a #define LOG_SELECT 0x4c diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 85ec8beb3157..a004e1961a00 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -163,4 +163,6 @@ extern int icnss_get_driver_load_cnt(void); extern void icnss_increment_driver_load_cnt(void); extern void icnss_set_cc_source(enum cnss_cc_src cc_source); extern enum cnss_cc_src icnss_get_cc_source(void); +extern void cnss_set_fw_version(u32 version); + #endif /* _ICNSS_WLAN_H_ */ diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h index 4117b0d47b0d..50a84bce8d9d 100644 --- a/include/soc/qcom/smem.h +++ b/include/soc/qcom/smem.h @@ -107,6 +107,8 @@ enum { SMEM_ID_VENDOR0, SMEM_ID_VENDOR1, SMEM_ID_VENDOR2, +/*For more details, could check boot_iamges/core/api/mproc/smem_type.h*/ + SMEM_PROJECT_INFO = 136, SMEM_HW_SW_BUILD_ID, SMEM_SMD_BASE_ID_2, SMEM_SMD_FIFO_BASE_ID_2 = SMEM_SMD_BASE_ID_2 + diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index a9d1772199bf..14e26174c5e7 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -29,6 +29,8 @@ #define NETLINK_CRYPTO 21 /* Crypto layer */ #define NETLINK_SOCKEV 22 /* Socket Administrative Events */ #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG +#define NETLINK_ADB 23 + #define MAX_LINKS 32 diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h index 7fe799e867d4..e75e1b6ff27f 100644 --- a/include/uapi/linux/time.h +++ b/include/uapi/linux/time.h @@ -56,7 +56,6 @@ struct itimerval { #define CLOCK_BOOTTIME_ALARM 9 #define CLOCK_SGI_CYCLE 10 /* Hardware specific */ #define CLOCK_TAI 11 -#define CLOCK_POWEROFF_ALARM 12 #define MAX_CLOCKS 16 #define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC) diff --git a/init/Kconfig b/init/Kconfig index a73b64531d69..4c7100c1f241 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -89,7 +89,7 @@ config LOCALVERSION config LOCALVERSION_AUTO bool "Automatically append version information to the version string" - default y + default n help This will try to automatically determine if the current tree is a release tree by looking for git tags that belong to the current @@ -834,7 +834,7 @@ config IKCONFIG_PROC config LOG_BUF_SHIFT int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" range 12 25 - default 17 + default 18 depends on PRINTK help Select the minimal kernel log buffer size as a power of 2. diff --git a/kernel/audit.c b/kernel/audit.c index d440c25cb3be..a22670697c98 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1505,6 +1505,10 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) if (!ab) return; + + if (console_loglevel < 4) + return; + va_start(args, fmt); audit_log_vformat(ab, fmt, args); va_end(args); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index cd3d81961cc2..f24659ddc617 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2691,6 +2691,7 @@ static int cgroup_procs_write_permission(struct task_struct *task, * need to check permissions on one of them. */ if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && + !uid_eq(cred->euid, GLOBAL_SYSTEM_UID) && !uid_eq(cred->euid, tcred->uid) && !uid_eq(cred->euid, tcred->suid) && !ns_capable(tcred->user_ns, CAP_SYS_NICE)) @@ -5420,7 +5421,7 @@ static int __init cgroup_wq_init(void) * We would prefer to do this in cgroup_init() above, but that * is called before init_workqueues(): so leave this until after. */ - cgroup_destroy_wq = alloc_workqueue("cgroup_destroy", 0, 1); + cgroup_destroy_wq = create_workqueue("cgroup_destroy"); BUG_ON(!cgroup_destroy_wq); /* diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 2d3df82c54f2..b114514ca5ee 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -43,6 +43,7 @@ enum freezer_state_flags { struct freezer { struct cgroup_subsys_state css; unsigned int state; + unsigned int oem_freeze_flag; }; static DEFINE_MUTEX(freezer_mutex); @@ -322,7 +323,8 @@ static void freeze_cgroup(struct freezer *freezer) css_task_iter_start(&freezer->css, &it); while ((task = css_task_iter_next(&it))) - freeze_task(task); + //huruihuan add for freezing task in cgroup despite of PF_FREEZER_SKIP flag + freeze_cgroup_task(task); css_task_iter_end(&it); } @@ -330,11 +332,23 @@ static void unfreeze_cgroup(struct freezer *freezer) { struct css_task_iter it; struct task_struct *task; + struct task_struct *tmp_tsk = NULL; + struct task_struct *g, *p; css_task_iter_start(&freezer->css, &it); - while ((task = css_task_iter_next(&it))) + while ((task = css_task_iter_next(&it))) { + tmp_tsk = task; __thaw_task(task); + } css_task_iter_end(&it); +/*make sure all the thread of one uid been wake up by huruihuan*/ + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (tmp_tsk && + p->real_cred->uid.val == tmp_tsk->real_cred->uid.val) + __thaw_task(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); } /** @@ -392,6 +406,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) * CGROUP_FREEZING_PARENT. */ mutex_lock(&freezer_mutex); + freezer->oem_freeze_flag = freeze ? 1 : 0; rcu_read_lock(); css_for_each_descendant_pre(pos, &freezer->css) { struct freezer *pos_f = css_freezer(pos); @@ -416,6 +431,29 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) mutex_unlock(&freezer_mutex); } +void unfreezer_fork(struct task_struct *task) +{ + struct freezer *freezer = NULL; + + /* + * The root cgroup is non-freezable, so we can skip locking the + */ + if (task_css_is_root(task, freezer_cgrp_id)) + return; + + rcu_read_lock(); + freezer = task_freezer(task); + rcu_read_unlock(); + + /* Only unfreeze the "writed FROZEN" group + */ + if (freezer->oem_freeze_flag != 1) + return; + + pr_debug("%s:%s(%d)try to unfreeze\n", __func__, task->comm, task->pid); + freezer_change_state(freezer, 0); +} + static ssize_t freezer_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { diff --git a/kernel/freezer.c b/kernel/freezer.c index a8900a3bc27a..4c83383da5a5 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -71,7 +71,8 @@ bool __refrigerator(bool check_kthr_stop) spin_lock_irq(&freezer_lock); current->flags |= PF_FROZEN; if (!freezing(current) || - (check_kthr_stop && kthread_should_stop())) + //huruihuan add for kill task in D status + (check_kthr_stop && kthread_should_stop()) || current->kill_flag) current->flags &= ~PF_FROZEN; spin_unlock_irq(&freezer_lock); @@ -104,6 +105,27 @@ static void fake_signal_wake_up(struct task_struct *p) } } +//huruihuan add for freezing task in cgroup despite of PF_FREEZER_SKIP flag +bool freeze_cgroup_task(struct task_struct *p) +{ + unsigned long flags; + + spin_lock_irqsave(&freezer_lock, flags); + if (!freezing(p) || frozen(p)) { + spin_unlock_irqrestore(&freezer_lock, flags); + return false; + } + + if (!(p->flags & PF_KTHREAD)) + fake_signal_wake_up(p); + else + wake_up_state(p, TASK_INTERRUPTIBLE); + + spin_unlock_irqrestore(&freezer_lock, flags); + return true; +} + + /** * freeze_task - send a freeze request to given task * @p: task to send the request to diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ec72aeb6defe..bdca0e0f3cce 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -634,6 +634,43 @@ int irq_set_irq_wake(unsigned int irq, unsigned int on) } EXPORT_SYMBOL(irq_set_irq_wake); +static int mask_wake_irq_set(const char *buff, const struct kernel_param *kp) +{ + char buf[256], *b; + char *irq_num_str; + unsigned int irq_num; + + pr_info("mask_wake_irq_set: %s\n",buff); + strlcpy(buf, buff, sizeof(buf)); + b = strim(buf); + + while (b) { + irq_num_str = strsep(&b, ","); + if (irq_num_str) { + sscanf(irq_num_str, "%u", &irq_num); + pr_info("mask_wake_irq_set irq_num: %d\n", irq_num); + irq_set_irq_wake(irq_num, 0); + } + } + + return 0; +} + +static int mask_wake_irq_get(char *buff, const struct kernel_param *kp) +{ + int cnt = 0; + cnt += snprintf(buff + cnt, PAGE_SIZE - cnt, "haha\n"); + return cnt; +} + +static const struct kernel_param_ops mask_wake_irq_ops = { + .set = mask_wake_irq_set, + .get = mask_wake_irq_get, +}; + +module_param_cb(mask_wake_irq, &mask_wake_irq_ops, NULL, 0644); + + /* * Internal function that tells the architecture code whether a * particular irq has been exclusively allocated or is available diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c index d381f559e0ce..039e7cc07e7e 100644 --- a/kernel/locking/spinlock_debug.c +++ b/kernel/locking/spinlock_debug.c @@ -113,8 +113,7 @@ static inline void debug_spin_unlock(raw_spinlock_t *lock) static void __spin_lock_debug(raw_spinlock_t *lock) { u64 i; - u64 loops = loops_per_jiffy * HZ; - + u64 loops = loops_per_jiffy * HZ * 5; for (i = 0; i < loops; i++) { if (arch_spin_trylock(&lock->raw_lock)) return; diff --git a/kernel/power/main.c b/kernel/power/main.c index 5ea50b1b7595..b4b1a2cb1f80 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -15,9 +15,29 @@ #include #include #include +#include +#include #include "power.h" +/* Define number of little/big cpu's and frequency +values based on projects like 8996/8998/sdm845 etc */ +#if defined(CONFIG_ARCH_MSM8998) +#define LITTLE_CLUSTER_CPU_NUMBER 0 +#define BIG_CLUSTER_CPU_NUMBER 4 +#define RESUME_BOOST_LITTLE_CPU_QOS_FREQ 1900800 +#define RESUME_BOOST_BIG_CPU_QOS_FREQ 2361600 +#elif defined(CONFIG_ARCH_MSM8996) +#define LITTLE_CLUSTER_CPU_NUMBER 0 +#define BIG_CLUSTER_CPU_NUMBER 2 +#define RESUME_BOOST_LITTLE_CPU_QOS_FREQ 1593600 +#define RESUME_BOOST_BIG_CPU_QOS_FREQ 2073600 +#endif +static struct pm_qos_request resumeboost_little_cpu_qos; +static struct pm_qos_request resumeboost_big_cpu_qos; +extern int get_resume_wakeup_flag(void); +extern int get_qpnp_kpdpwr_resume_wakeup_flag(void); + DEFINE_MUTEX(pm_mutex); #ifdef CONFIG_PM_SLEEP @@ -352,6 +372,31 @@ static suspend_state_t decode_state(const char *buf, size_t n) return PM_SUSPEND_ON; } +void resumeboost_fn(void) +{ + struct cpufreq_policy *policy; + + if(get_resume_wakeup_flag() || get_qpnp_kpdpwr_resume_wakeup_flag()) { + /* Fetch little cpu policy and drive the CPU towards target frequency */ + policy = cpufreq_cpu_get(LITTLE_CLUSTER_CPU_NUMBER); + if (policy) { + cpufreq_driver_target(policy, RESUME_BOOST_LITTLE_CPU_QOS_FREQ, CPUFREQ_RELATION_H); + pm_qos_update_request_timeout(&resumeboost_little_cpu_qos, MAX_CPUFREQ, 1000000); + } else + return; + cpufreq_cpu_put(policy); + + /* Fetch big cpu policy and drive big cpu towards target frequency */ + policy = cpufreq_cpu_get(BIG_CLUSTER_CPU_NUMBER); + if (policy) { + cpufreq_driver_target(policy, RESUME_BOOST_BIG_CPU_QOS_FREQ, CPUFREQ_RELATION_H); + pm_qos_update_request_timeout(&resumeboost_big_cpu_qos, MAX_CPUFREQ-1, 1000000); + } else + return; + cpufreq_cpu_put(policy); + } +} + static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { @@ -377,6 +422,7 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, out: pm_autosleep_unlock(); + resumeboost_fn(); return error ? error : n; } @@ -655,3 +701,12 @@ static int __init pm_init(void) } core_initcall(pm_init); + +static int __init init_pm_qos(void) +{ + pm_qos_add_request(&resumeboost_little_cpu_qos, PM_QOS_C0_CPUFREQ_MIN, MIN_CPUFREQ); + pm_qos_add_request(&resumeboost_big_cpu_qos, PM_QOS_C1_CPUFREQ_MIN, MIN_CPUFREQ); + + return 0; +} +late_initcall(init_pm_qos); diff --git a/kernel/power/power.h b/kernel/power/power.h index 2610516601ee..7afa7bc6db14 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -294,3 +294,6 @@ extern int pm_wake_lock(const char *buf); extern int pm_wake_unlock(const char *buf); #endif /* !CONFIG_PM_WAKELOCKS */ + +/*huruihuan add for speed up resume*/ +extern void thaw_fingerprintd(void); diff --git a/kernel/power/process.c b/kernel/power/process.c index cc177142a08f..9d37c4e35955 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -20,6 +20,7 @@ #include #include #include +#include /* * Timeout for stopping processes @@ -191,6 +192,32 @@ int freeze_kernel_threads(void) return error; } +/*huruihuan add for speed up resume*/ +void thaw_fingerprintd(void) +{ + struct task_struct *g, *p; + struct task_struct *curr = current; + + pm_freezing = false; + pm_nosig_freezing = false; + if (fp_irq_cnt) { + fp_irq_cnt = false; + c1_cpufreq_limit_queue(); + } + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + /* No other threads should have PF_SUSPEND_TASK set */ + WARN_ON((p != curr) && (p->flags & PF_SUSPEND_TASK)); + if (!memcmp(p->comm, "fingerprintd", 13)) + __thaw_task(p); + if (!memcmp(p->comm, "fingerprintmsg", 15)) + __thaw_task(p); + } + read_unlock(&tasklist_lock); + pm_freezing = true; + pm_nosig_freezing = true; +} + void thaw_processes(void) { struct task_struct *g, *p; diff --git a/kernel/power/qos.c b/kernel/power/qos.c index e6eceb0aa496..69fd57ff7a00 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -129,6 +129,102 @@ static struct pm_qos_object memory_bandwidth_pm_qos = { .name = "memory_bandwidth", }; +static BLOCKING_NOTIFIER_HEAD(c0_cpufreq_max_notifier); +static struct pm_qos_constraints c0_cpufreq_max_constraints = { + .list = PLIST_HEAD_INIT(c0_cpufreq_max_constraints.list), + .target_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE }, + .default_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .type = PM_QOS_MIN, + .notifiers = &c0_cpufreq_max_notifier, +}; +static struct pm_qos_object c0_cpufreq_max_pm_qos = { + .constraints = &c0_cpufreq_max_constraints, + .name = "c0_cpufreq_max", +}; + + +static BLOCKING_NOTIFIER_HEAD(c0_cpufreq_min_notifier); +static struct pm_qos_constraints c0_cpufreq_min_constraints = { + .list = PLIST_HEAD_INIT(c0_cpufreq_min_constraints.list), + .target_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE }, + .default_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .type = PM_QOS_MAX, + .notifiers = &c0_cpufreq_min_notifier, +}; +static struct pm_qos_object c0_cpufreq_min_pm_qos = { + .constraints = &c0_cpufreq_min_constraints, + .name = "c0_cpufreq_min", +}; + +static BLOCKING_NOTIFIER_HEAD(c1_cpufreq_max_notifier); +static struct pm_qos_constraints c1_cpufreq_max_constraints = { + .list = PLIST_HEAD_INIT(c1_cpufreq_max_constraints.list), + .target_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE }, + .default_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPUFREQ_MAX_DEFAULT_VALUE, + .type = PM_QOS_MIN, + .notifiers = &c1_cpufreq_max_notifier, +}; +static struct pm_qos_object c1_cpufreq_max_pm_qos = { + .constraints = &c1_cpufreq_max_constraints, + .name = "c1_cpufreq_max", +}; + +static BLOCKING_NOTIFIER_HEAD(c1_cpufreq_min_notifier); +static struct pm_qos_constraints c1_cpufreq_min_constraints = { + .list = PLIST_HEAD_INIT(c1_cpufreq_min_constraints.list), + .target_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE }, + .default_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPUFREQ_MIN_DEFAULT_VALUE, + .type = PM_QOS_MAX, + .notifiers = &c1_cpufreq_min_notifier, +}; +static struct pm_qos_object c1_cpufreq_min_pm_qos = { + .constraints = &c1_cpufreq_min_constraints, + .name = "c1_cpufreq_min", +}; + +static BLOCKING_NOTIFIER_HEAD(devfreq_max_notifier); +static struct pm_qos_constraints devfreq_max_constraints = { + .list = PLIST_HEAD_INIT(devfreq_max_constraints.list), + .target_value = PM_QOS_DEVFREQ_MAX_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_DEVFREQ_MAX_DEFAULT_VALUE }, + .default_value = PM_QOS_DEVFREQ_MAX_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_DEVFREQ_MAX_DEFAULT_VALUE, + .type = PM_QOS_MIN, + .notifiers = &devfreq_max_notifier, +}; +static struct pm_qos_object devfreq_max_pm_qos = { + .constraints = &devfreq_max_constraints, + .name = "devfreq_max", +}; + +static BLOCKING_NOTIFIER_HEAD(devfreq_min_notifier); +static struct pm_qos_constraints devfreq_min_constraints = { + .list = PLIST_HEAD_INIT(devfreq_min_constraints.list), + .target_value = PM_QOS_DEVFREQ_MIN_DEFAULT_VALUE, + .target_per_cpu = { [0 ... (NR_CPUS - 1)] = + PM_QOS_DEVFREQ_MIN_DEFAULT_VALUE }, + .default_value = PM_QOS_DEVFREQ_MIN_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_DEVFREQ_MIN_DEFAULT_VALUE, + .type = PM_QOS_MAX, + .notifiers = &devfreq_min_notifier, +}; +static struct pm_qos_object devfreq_min_pm_qos = { + .constraints = &devfreq_min_constraints, + .name = "devfreq_min", +}; static struct pm_qos_object *pm_qos_array[] = { &null_pm_qos, @@ -136,6 +232,12 @@ static struct pm_qos_object *pm_qos_array[] = { &network_lat_pm_qos, &network_throughput_pm_qos, &memory_bandwidth_pm_qos, + &c0_cpufreq_max_pm_qos, + &c0_cpufreq_min_pm_qos, + &c1_cpufreq_max_pm_qos, + &c1_cpufreq_min_pm_qos, + &devfreq_max_pm_qos, + &devfreq_min_pm_qos, }; static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, @@ -624,12 +726,12 @@ void pm_qos_add_request(struct pm_qos_request *req, cpumask_setall(&req->cpus_affine); break; } - - req->pm_qos_class = pm_qos_class; INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); trace_pm_qos_add_request(pm_qos_class, value); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, req, PM_QOS_ADD_REQ, value); + /* Fixes rare panic */ + req->pm_qos_class = pm_qos_class; #ifdef CONFIG_SMP if (req->type == PM_QOS_REQ_AFFINE_IRQ && diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 58209d8bfc56..269b64177c61 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include "../../drivers/pinctrl/qcom/pinctrl-msm.h" #include "power.h" @@ -384,6 +386,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) trace_suspend_resume(TPS("machine_suspend"), state, false); events_check_enabled = false; + need_show_pinctrl_irq = true; } else if (*wakeup) { pm_get_active_wakeup_sources(suspend_abort, MAX_SUSPEND_ABORT_LEN); @@ -401,6 +404,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) Platform_wake: platform_resume_noirq(state); +/*huruihuan add for speed up resume*/ + thaw_fingerprintd(); dpm_resume_noirq(PMSG_RESUME); Platform_early_resume: diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index da573aeaeb12..01cd740d0545 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,23 @@ extern void printascii(char *); #endif +/* +*zyh we use dynamic add console , +*so we can't use __init __exit, this will cause can't find func +*/ +#ifdef __init +#undef __init +#endif + +#ifdef __exit +#undef __exit +#endif + +#define __init +#define __exit + + + int console_printk[4] = { CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */ MESSAGE_LOGLEVEL_DEFAULT, /* default_message_loglevel */ @@ -925,6 +943,13 @@ static void __init log_buf_add_cpu(void) #else /* !CONFIG_SMP */ static inline void log_buf_add_cpu(void) {} #endif /* CONFIG_SMP */ +static int __init ftm_console_silent_setup(char *str) +{ + pr_info("ftm_silent_log \n"); + console_silent(); + return 0; +} +early_param("ftm_console_silent", ftm_console_silent_setup); void __init setup_log_buf(int early) { @@ -1041,21 +1066,9 @@ static inline void boot_delay_msec(int level) static bool printk_time = IS_ENABLED(CONFIG_PRINTK_TIME); module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR); -static size_t print_time(u64 ts, char *buf) -{ - unsigned long rem_nsec; +static bool print_wall_time = 1; +module_param_named(print_wall_time, print_wall_time, bool, S_IRUGO | S_IWUSR); - if (!printk_time) - return 0; - - rem_nsec = do_div(ts, 1000000000); - - if (!buf) - return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts); - - return sprintf(buf, "[%5lu.%06lu] ", - (unsigned long)ts, rem_nsec / 1000); -} static size_t print_prefix(const struct printk_log *msg, bool syslog, char *buf) { @@ -1075,8 +1088,6 @@ static size_t print_prefix(const struct printk_log *msg, bool syslog, char *buf) len++; } } - - len += print_time(msg->ts_nsec, buf ? buf + len : NULL); return len; } @@ -1638,8 +1649,6 @@ static size_t cont_print_text(char *text, size_t size) size_t len; if (cont.cons == 0 && (console_prev & LOG_NEWLINE)) { - textlen += print_time(cont.ts_nsec, text); - size -= textlen; } len = cont.len - cont.cons; @@ -1660,11 +1669,15 @@ static size_t cont_print_text(char *text, size_t size) return textlen; } +void getnstimeofday64(struct timespec64 *ts); + asmlinkage int vprintk_emit(int facility, int level, const char *dict, size_t dictlen, const char *fmt, va_list args) { static int recursion_bug; + static char texttmp[LOG_LINE_MAX]; + static bool last_new_line = true; static char textbuf[LOG_LINE_MAX]; char *text = textbuf; size_t text_len = 0; @@ -1675,6 +1688,10 @@ asmlinkage int vprintk_emit(int facility, int level, bool in_sched = false; /* cpu currently holding logbuf_lock in this function */ static unsigned int logbuf_cpu = UINT_MAX; + u64 ts_sec = local_clock(); + unsigned long rem_nsec; + + rem_nsec = do_div(ts_sec, 1000000000); if (level == LOGLEVEL_SCHED) { level = LOGLEVEL_DEFAULT; @@ -1758,6 +1775,45 @@ asmlinkage int vprintk_emit(int facility, int level, } } + if (last_new_line) { + if (print_wall_time && ts_sec >= 20) { + struct timespec64 tspec; + struct rtc_time tm; + extern struct timezone sys_tz; + + __getnstimeofday64(&tspec); + /*utc + timezone add by huoyinghui@160425*/ + if (sys_tz.tz_minuteswest < 0 || (tspec.tv_sec - sys_tz.tz_minuteswest*60) >= 0) + tspec.tv_sec -= sys_tz.tz_minuteswest * 60; + rtc_time_to_tm(tspec.tv_sec, &tm); + + text_len = scnprintf(texttmp, sizeof(texttmp), "[%02d%02d%02d_%02d:%02d:%02d.%06ld]@%d %s", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, + tspec.tv_nsec / 1000, this_cpu, text); + } + else { + + text_len = scnprintf(texttmp, sizeof(texttmp), "[%5lu.%06lu]@%d %s", + (unsigned long)ts_sec, rem_nsec / 1000, this_cpu, text); + } + + text = texttmp; + + /* mark and strip a trailing newline */ + if (text_len && text[text_len-1] == '\n') { + text_len--; + lflags |= LOG_NEWLINE; + } + } + + if (lflags & LOG_NEWLINE) { + last_new_line = true; + } + else { + last_new_line = false; + } + + #ifdef CONFIG_EARLY_PRINTK_DIRECT printascii(text); #endif @@ -2063,6 +2119,13 @@ static int __init console_setup(char *str) } __setup("console=", console_setup); +int force_oem_console_setup(char *str) +{ + console_setup(str); + return 1; +} +EXPORT_SYMBOL(force_oem_console_setup); + /** * add_preferred_console - add a device to the list of preferred consoles. * @name: device name diff --git a/kernel/reboot.c b/kernel/reboot.c index bd30a973fe94..eff59767e84b 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include /* * this indicates whether you can reboot with ctrl-alt-del: the default is yes @@ -44,6 +46,7 @@ int reboot_cpu; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; + /* * If set, this is used for preparing the system to power off. */ @@ -220,6 +223,17 @@ void kernel_restart(char *cmd) pr_emerg("Restarting system\n"); else pr_emerg("Restarting system with command '%s'\n", cmd); + + /*if enable dump, if dm-verity device corrupted, force enter dump */ + if (oem_get_download_mode()) { + if (((cmd != NULL && cmd[0] != '\0') && + !strcmp(cmd, "dm-verity device corrupted"))) { + panic("dm-verity device corrupted Force Dump"); + pr_emerg("Restarting system painc\n"); + msleep(10000); + } + } + kmsg_dump(KMSG_DUMP_RESTART); machine_restart(cmd); } diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index ddcf7cfb7248..1c5428c74f96 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -4305,7 +4305,7 @@ int update_preferred_cluster(struct related_thread_group *grp, { u32 new_load = task_load(p); - if (!grp) + if (!grp || !p->grp) return 0; /* diff --git a/kernel/signal.c b/kernel/signal.c index 8bfbc47f0a23..79d1a56e1564 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -979,6 +979,19 @@ static inline void userns_fixup_signal_uid(struct siginfo *info, struct task_str } #endif +static int print_key_process_murder __read_mostly = 1; + +static bool is_zygote_process(struct task_struct *t) +{ + const struct cred *tcred = __task_cred(t); + + if(!strcmp(t->comm, "main") && (tcred->uid.val == 0) && (t->parent != 0 && !strcmp(t->parent->comm,"init"))) + return true; + else + return false; + return false; +} + static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, int group, int from_ancestor_ns) { @@ -990,6 +1003,18 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t, assert_spin_locked(&t->sighand->siglock); result = TRACE_SIGNAL_IGNORED; + + if(print_key_process_murder) { + if(!strcmp(t->comm, "system_server") || + is_zygote_process(t) || + !strcmp(t->comm, "surfaceflinger") || + !strcmp(t->comm, "servicemanager")) + { + struct task_struct *tg = current->group_leader; + printk("process %d:%s, %d:%s send sig:%d to process %d:%s\n", + tg->pid, tg->comm, current->pid, current->comm, sig, t->pid, t->comm); + } + } if (!prepare_signal(sig, t, from_ancestor_ns || (info == SEND_SIG_FORCED))) goto ret; @@ -1146,7 +1171,19 @@ int do_send_sig_info(int sig, struct siginfo *info, struct task_struct *p, { unsigned long flags; int ret = -ESRCH; - + //huruihuan add for kill task in D status + if(sig == SIGKILL){ + if(p && p->flags & PF_FROZEN){ + struct task_struct *child = p; + rcu_read_lock(); + do{ + child = next_thread(child); + child->kill_flag = 1; + __thaw_task(child); + }while(child !=p); + rcu_read_unlock(); + } + } if (lock_task_sighand(p, &flags)) { ret = send_signal(sig, info, p, group); unlock_task_sighand(p, &flags); @@ -2868,6 +2905,7 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese, SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) { struct siginfo info; + struct task_struct *p; info.si_signo = sig; info.si_errno = 0; @@ -2875,6 +2913,11 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) info.si_pid = task_tgid_vnr(current); info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); + if (sig == SIGQUIT || sig == SIGSEGV || sig == SIGABRT) { + p = pid_task(find_vpid(pid), PIDTYPE_PID); + if (p) + unfreezer_fork(p); + } return kill_something_info(sig, &info, pid); } diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index ceec77c652b5..86152d44d098 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -29,7 +29,6 @@ #ifdef CONFIG_MSM_PM #include "lpm-levels.h" #endif -#include /** * struct alarm_base - Alarm timer bases @@ -51,116 +50,12 @@ static ktime_t freezer_delta; static DEFINE_SPINLOCK(freezer_delta_lock); static struct wakeup_source *ws; -static struct delayed_work work; -static struct workqueue_struct *power_off_alarm_workqueue; #ifdef CONFIG_RTC_CLASS /* rtc timer and device for setting alarm wakeups at suspend */ static struct rtc_timer rtctimer; static struct rtc_device *rtcdev; static DEFINE_SPINLOCK(rtcdev_lock); -static struct mutex power_on_alarm_lock; -static struct alarm init_alarm; - -/** - * power_on_alarm_init - Init power on alarm value - * - * Read rtc alarm value after device booting up and add this alarm - * into alarm queue. - */ -void power_on_alarm_init(void) -{ - struct rtc_wkalrm rtc_alarm; - struct rtc_time rt; - unsigned long alarm_time; - struct rtc_device *rtc; - ktime_t alarm_ktime; - - rtc = alarmtimer_get_rtcdev(); - - if (!rtc) - return; - - rtc_read_alarm(rtc, &rtc_alarm); - rt = rtc_alarm.time; - - rtc_tm_to_time(&rt, &alarm_time); - - if (alarm_time) { - alarm_ktime = ktime_set(alarm_time, 0); - alarm_init(&init_alarm, ALARM_POWEROFF_REALTIME, NULL); - alarm_start(&init_alarm, alarm_ktime); - } -} - -/** - * set_power_on_alarm - set power on alarm value into rtc register - * - * Get the soonest power off alarm timer and set the alarm value into rtc - * register. - */ -void set_power_on_alarm(void) -{ - int rc; - struct timespec wall_time, alarm_ts; - long alarm_secs = 0l; - long rtc_secs, alarm_time, alarm_delta; - struct rtc_time rtc_time; - struct rtc_wkalrm alarm; - struct rtc_device *rtc; - struct timerqueue_node *next; - unsigned long flags; - struct alarm_base *base = &alarm_bases[ALARM_POWEROFF_REALTIME]; - - rc = mutex_lock_interruptible(&power_on_alarm_lock); - if (rc != 0) - return; - - spin_lock_irqsave(&base->lock, flags); - next = timerqueue_getnext(&base->timerqueue); - spin_unlock_irqrestore(&base->lock, flags); - - if (next) { - alarm_ts = ktime_to_timespec(next->expires); - alarm_secs = alarm_ts.tv_sec; - } - - if (!alarm_secs) - goto disable_alarm; - - getnstimeofday(&wall_time); - - /* - * alarm_secs have to be bigger than "wall_time +1". - * It is to make sure that alarm time will be always - * bigger than wall time. - */ - if (alarm_secs <= wall_time.tv_sec + 1) - goto disable_alarm; - - rtc = alarmtimer_get_rtcdev(); - if (!rtc) - goto exit; - - rtc_read_time(rtc, &rtc_time); - rtc_tm_to_time(&rtc_time, &rtc_secs); - alarm_delta = wall_time.tv_sec - rtc_secs; - alarm_time = alarm_secs - alarm_delta; - - rtc_time_to_tm(alarm_time, &alarm.time); - alarm.enabled = 1; - rc = rtc_set_alarm(rtcdev, &alarm); - if (rc) - goto disable_alarm; - - mutex_unlock(&power_on_alarm_lock); - return; - -disable_alarm: - rtc_alarm_irq_enable(rtcdev, 0); -exit: - mutex_unlock(&power_on_alarm_lock); -} static void alarmtimer_triggered_func(void *p) { @@ -232,8 +127,6 @@ static void alarmtimer_rtc_remove_device(struct device *dev, static inline void alarmtimer_rtc_timer_init(void) { - mutex_init(&power_on_alarm_lock); - rtc_timer_init(&rtctimer, NULL, NULL); } @@ -260,14 +153,8 @@ struct rtc_device *alarmtimer_get_rtcdev(void) static inline int alarmtimer_rtc_interface_setup(void) { return 0; } static inline void alarmtimer_rtc_interface_remove(void) { } static inline void alarmtimer_rtc_timer_init(void) { } -void set_power_on_alarm(void) { } #endif -static void alarm_work_func(struct work_struct *unused) -{ - set_power_on_alarm(); -} - /** * alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue * @base: pointer to the base where the timer is being run @@ -337,10 +224,6 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer) } spin_unlock_irqrestore(&base->lock, flags); - /* set next power off alarm */ - if (alarm->type == ALARM_POWEROFF_REALTIME) - queue_delayed_work(power_off_alarm_workqueue, &work, 0); - return ret; } @@ -434,8 +317,6 @@ static int alarmtimer_suspend(struct device *dev) int i; int ret; - cancel_delayed_work_sync(&work); - spin_lock_irqsave(&freezer_delta_lock, flags); min = freezer_delta; freezer_delta = ktime_set(0, 0); @@ -492,7 +373,6 @@ static int alarmtimer_resume(struct device *dev) return 0; rtc_timer_cancel(rtc, &rtctimer); - queue_delayed_work(power_off_alarm_workqueue, &work, 0); return 0; } @@ -673,14 +553,12 @@ EXPORT_SYMBOL_GPL(alarm_forward_now); * clock2alarm - helper that converts from clockid to alarmtypes * @clockid: clockid. */ -enum alarmtimer_type clock2alarm(clockid_t clockid) +static enum alarmtimer_type clock2alarm(clockid_t clockid) { if (clockid == CLOCK_REALTIME_ALARM) return ALARM_REALTIME; if (clockid == CLOCK_BOOTTIME_ALARM) return ALARM_BOOTTIME; - if (clockid == CLOCK_POWEROFF_ALARM) - return ALARM_POWEROFF_REALTIME; return -1; } @@ -1076,13 +954,10 @@ static int __init alarmtimer_init(void) posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock); posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock); - posix_timers_register_clock(CLOCK_POWEROFF_ALARM, &alarm_clock); /* Initialize alarm bases */ alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME; alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real; - alarm_bases[ALARM_POWEROFF_REALTIME].base_clockid = CLOCK_REALTIME; - alarm_bases[ALARM_POWEROFF_REALTIME].gettime = &ktime_get_real; alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME; alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime; for (i = 0; i < ALARM_NUMTYPE; i++) { @@ -1104,24 +979,8 @@ static int __init alarmtimer_init(void) goto out_drv; } ws = wakeup_source_register("alarmtimer"); - if (!ws) { - error = -ENOMEM; - goto out_ws; - } - - INIT_DELAYED_WORK(&work, alarm_work_func); - power_off_alarm_workqueue = - create_singlethread_workqueue("power_off_alarm"); - if (!power_off_alarm_workqueue) { - error = -ENOMEM; - goto out_wq; - } - return 0; -out_wq: - wakeup_source_unregister(ws); -out_ws: - platform_device_unregister(pdev); + out_drv: platform_driver_unregister(&alarmtimer_driver); out_if: diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index b30adaae739a..8e389edc28db 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2127,22 +2127,6 @@ void trace_printk_init_buffers(void) return; /* trace_printk() is for debug use only. Don't use it in production. */ - - pr_warning("\n"); - pr_warning("**********************************************************\n"); - pr_warning("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); - pr_warning("** **\n"); - pr_warning("** trace_printk() being used. Allocating extra memory. **\n"); - pr_warning("** **\n"); - pr_warning("** This means that this is a DEBUG kernel and it is **\n"); - pr_warning("** unsafe for production use. **\n"); - pr_warning("** **\n"); - pr_warning("** If you see this message and you are not debugging **\n"); - pr_warning("** the kernel, report this immediately to your vendor! **\n"); - pr_warning("** **\n"); - pr_warning("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); - pr_warning("**********************************************************\n"); - /* Expand the buffers to set size */ tracing_update_buffers(); diff --git a/mm/kmemleak.c b/mm/kmemleak.c index de479ce41690..80389e501157 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -137,6 +137,7 @@ struct kmemleak_scan_area { #define KMEMLEAK_GREY 0 #define KMEMLEAK_BLACK -1 +int oem_kmemleak_flag; /* * Structure holding the metadata for each allocated memory block. * Modifications to such objects should be made while holding the @@ -1871,7 +1872,7 @@ void __init kmemleak_init(void) { int i; unsigned long flags; - + if(0 == oem_kmemleak_flag){ #ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF if (!kmemleak_skip_disable) { kmemleak_early_log = 0; @@ -1879,6 +1880,9 @@ void __init kmemleak_init(void) return; } #endif + } + + pr_info("Kernel memory leak detector enabled\n"); jiffies_min_age = msecs_to_jiffies(MSECS_MIN_AGE); jiffies_scan_wait = msecs_to_jiffies(SECS_SCAN_WAIT * 1000); @@ -1957,6 +1961,10 @@ static int __init kmemleak_late_init(void) { struct dentry *dentry; + if (!kmemleak_skip_disable) { + kmemleak_disable(); + return 0; + } kmemleak_initialized = 1; if (kmemleak_error) { @@ -1983,3 +1991,22 @@ static int __init kmemleak_late_init(void) return 0; } late_initcall(kmemleak_late_init); + +static int __init kmem_leak_detect(char *str) +{ + if(str == NULL){ + return -1; + } + + if (strcmp(str, "true") == 0){ + kmemleak_skip_disable = 1; + oem_kmemleak_flag=1; + } + else { + kmemleak_disable(); + oem_kmemleak_flag=0; + } + return 0; +} + +early_param("kmemleak_detect", kmem_leak_detect); diff --git a/mm/page_poison.c b/mm/page_poison.c index c8cf230dbfcb..fe456f1b4e71 100644 --- a/mm/page_poison.c +++ b/mm/page_poison.c @@ -80,10 +80,12 @@ static void check_poison_mem(struct page *page, if (!__ratelimit(&ratelimit)) return; - else if (start == end && single_bit_flip(*start, PAGE_POISON)) + else if (start == end && single_bit_flip(*start, PAGE_POISON)) { pr_err("pagealloc: single bit error on page with phys start 0x%lx\n", (unsigned long)page_to_phys(page)); - else + pr_err("virt: %p, phys: 0x%llx\n", start, + virt_to_phys(start)); + } else pr_err("pagealloc: memory corruption on page with phys start 0x%lx\n", (unsigned long)page_to_phys(page)); diff --git a/net/wireless/db.txt b/net/wireless/db.txt index 499d98745d44..c3160eedda93 100644 --- a/net/wireless/db.txt +++ b/net/wireless/db.txt @@ -1,10 +1,10 @@ # This is the world regulatory domain country 00: - (2402 - 2472 @ 40), (20) + (2402 - 2472 @ 40), (23) # Channel 12 - 13. - (2457 - 2482 @ 40), (20), PASSIVE-SCAN, NO-IBSS + (2457 - 2482 @ 40), (23), PASSIVE-SCAN, NO-IBSS # Channel 14. Only JP enables this and for 802.11b only - (2474 - 2494 @ 20), (20), PASSIVE-SCAN, NO-IBSS, NO-OFDM + (2474 - 2494 @ 20), (23), PASSIVE-SCAN, NO-IBSS, NO-OFDM # Channel 36 - 48 (5170 - 5250 @ 80), (20), PASSIVE-SCAN, NO-IBSS (5250 - 5330 @ 80), (20), PASSIVE-SCAN, NO-IBSS @@ -17,40 +17,42 @@ country 00: country AE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country AF: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country AI: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country AL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5150 - 5250 @ 80), (23), AUTO-BW (5250 - 5350 @ 80), (23), DFS, AUTO-BW (5470 - 5710 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country AM: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 20), (18) (5250 - 5330 @ 20), (18), DFS country AN: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country AR: (2402 - 2482 @ 40), (36) @@ -70,10 +72,11 @@ country AS: DFS-FCC (5735 - 5850 @ 80), (30) country AT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5735 - 5835 @ 80), (30) # 5.9ghz band # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf (5850 - 5870 @ 10), (30) @@ -83,11 +86,13 @@ country AT: DFS-ETSI (5890 - 5910 @ 10), (30) (5900 - 5920 @ 10), (30) (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country AU: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5590 @ 80), (24), DFS @@ -97,80 +102,65 @@ country AU: DFS-FCC (57240 - 65880 @ 2160), (43), NO-OUTDOOR country AW: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country AZ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (18), AUTO-BW (5250 - 5330 @ 80), (18), DFS, AUTO-BW country BA: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country BB: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (30) country BD: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5735 - 5835 @ 80), (30) country BE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country BF: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country BG: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country BH: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 20), (23) (5735 - 5835 @ 20), (33) country BL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS @@ -183,13 +173,13 @@ country BM: DFS-FCC (5735 - 5835 @ 80), (30) country BN: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (20), AUTO-BW (5250 - 5330 @ 80), (20), DFS, AUTO-BW (5735 - 5835 @ 80), (20) country BO: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5250 - 5330 @ 80), (30), DFS (5735 - 5835 @ 80), (30) # 60 gHz band channels 1-3, FCC @@ -205,79 +195,72 @@ country BR: DFS-FCC (57240 - 63720 @ 2160), (40) country BS: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country BT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country BY: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country BZ: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (23) - (5490 - 5730 @ 160), (30) - (5735 - 5835 @ 80), (30) + (2402 - 2482 @ 40), (36) + (5170 - 5330 @ 160), (27) + (5490 - 5730 @ 160), (36) + (5735 - 5835 @ 80), (36) country CA: DFS-FCC (2402 - 2472 @ 40), (30) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW - (5490 - 5730 @ 160), (24), DFS + (5490 - 5590 @ 80), (24), DFS + (5650 - 5730 @ 80), (24), DFS (5735 - 5835 @ 80), (30) # 60 gHz band channels 1-3 (57240 - 63720 @ 2160), (40) country CF: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 40), (24) (5250 - 5330 @ 40), (24), DFS (5490 - 5730 @ 40), (24), DFS (5735 - 5835 @ 40), (30) country CH: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country CI: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country CL: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (20) (5735 - 5835 @ 80), (20) # 60 gHz band channels 1-3 (57240 - 63720 @ 2160), (50), NO-OUTDOOR country CN: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (33) @@ -285,14 +268,14 @@ country CN: DFS-FCC (59400 - 63720 @ 2160), (44) country CO: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country CR: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 20), (24) (5250 - 5330 @ 20), (24), DFS (5490 - 5730 @ 20), (24), DFS @@ -301,45 +284,29 @@ country CR: DFS-FCC (57240 - 63720 @ 2160), (30) country CX: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country CY: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) # Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf # and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf country CZ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -350,36 +317,20 @@ country CZ: DFS-ETSI country DE: DFS-ETSI # entries 279004 and 280006 - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country DK: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -396,13 +347,13 @@ country DO: DFS-FCC (5735 - 5835 @ 80), (30) country DZ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5670 @ 160), (23), DFS country EC: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 20), (24) (5250 - 5330 @ 20), (24), DFS (5490 - 5730 @ 20), (24), DFS @@ -411,64 +362,40 @@ country EC: DFS-FCC (57240 - 63720 @ 2160), (40) country EE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country EG: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 40), (23) (5250 - 5330 @ 40), (23), DFS country ES: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country ET: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country FI: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -480,36 +407,20 @@ country FM: DFS-FCC (5735 - 5835 @ 80), (30) country FR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country GB: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -521,18 +432,19 @@ country GD: DFS-FCC (5735 - 5835 @ 80), (30) country GE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (18), AUTO-BW (5250 - 5330 @ 80), (18), DFS, AUTO-BW country GF: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country GH: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -545,36 +457,28 @@ country GI: DFS-ETSI (5490 - 5710 @ 160), (30), DFS country GL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country GP: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country GR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country GT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS @@ -593,7 +497,7 @@ country GY: (5735 - 5835 @ 80), (30) country HK: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -602,7 +506,7 @@ country HK: DFS-FCC (57240 - 65880 @ 2160), (40) country HN: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (24) (5490 - 5730 @ 160), (24) (5735 - 5835 @ 80), (30) @@ -610,19 +514,11 @@ country HN: (57240 - 63720 @ 2160), (40) country HR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -634,19 +530,11 @@ country HT: DFS-FCC (5735 - 5835 @ 80), (30) country HU: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -656,76 +544,52 @@ country ID: (5735 - 5815 @ 20), (30) country IE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country IL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW # 60 gHz band channels 1-4, base on Etsi En 302 567 (57000 - 66000 @ 2160), (40) country IN: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (23) - (5735 - 5835 @ 80), (33) + (5735 - 5835 @ 80), (30) country IQ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country IS: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country IT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country JM: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -734,7 +598,7 @@ country JM: DFS-FCC (57240 - 63720 @ 2160), (40) country JO: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23) (5735 - 5835 @ 80), (23) # 60 gHz band channels 1-4, ref: Etsi En 302 567 @@ -742,178 +606,155 @@ country JO: country JP: DFS-JP (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR - (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR - (5490 - 5710 @ 160), (20), DFS + (5170 - 5250 @ 80), (23), AUTO-BW, NO-OUTDOOR + (5250 - 5330 @ 80), (26), DFS, AUTO-BW, NO-OUTDOOR + (5490 - 5710 @ 160), (26), DFS # 60 gHz band channels 1-4 (57240 - 65880 @ 2160), (40) country KE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23) (5490 - 5570 @ 80), (30), DFS (5735 - 5775 @ 40), (23) country KH: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country KN: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (30), DFS, AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS (5735 - 5815 @ 80), (30) country KR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5490 - 5730 @ 160), (30), DFS + (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5490 - 5710 @ 160), (30), DFS (5735 - 5835 @ 80), (30) # 60 GHz band channels 1-4, # ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99 (57240 - 65880 @ 2160), (43) country KW: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW country KY: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) -country KZ: - (2402 - 2482 @ 40), (20) +# Source: +# http://mic.gov.kz/sites/default/files/pages/pravila_prisvoeniya_polos_chastot_no34.pdf +# http://adilet.zan.kz/rus/docs/P000001379_ +country KZ: DFS-ETSI + (2402 - 2482 @ 40), (23) + (5150 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW + (5250 - 5350 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW + (5470 - 5725 @ 80), (20), NO-OUTDOOR, DFS country LB: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country LC: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (20), AUTO-BW (5250 - 5330 @ 80), (30), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS (5735 - 5815 @ 80), (30) country LI: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country LK: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 20), (24) (5250 - 5330 @ 20), (24), DFS (5490 - 5730 @ 20), (24), DFS (5735 - 5835 @ 20), (30) country LS: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country LT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country LU: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country LV: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country MA: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country MC: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MD: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country ME: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MF: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS @@ -926,20 +767,21 @@ country MH: DFS-FCC (5735 - 5835 @ 80), (30) country MK: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MN: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country MO: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -953,49 +795,48 @@ country MP: DFS-FCC (5735 - 5835 @ 80), (30) country MQ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country MT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country MU: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) +country MU: DFS-ETSI + (2402 - 2482 @ 40), (20) + (5170 - 5250 @ 80), (23), AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country MV: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (20), AUTO-BW (5250 - 5330 @ 80), (20), DFS, AUTO-BW (5735 - 5835 @ 80), (20) country MW: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS @@ -1010,7 +851,7 @@ country MX: DFS-FCC (57240 - 63720 @ 2160), (40) country MY: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5650 @ 160), (24), DFS @@ -1019,14 +860,14 @@ country MY: DFS-FCC (57240 - 63720 @ 2160), (40) country NA: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5730 @ 160), (30), DFS (5735 - 5835 @ 80), (33) country NG: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5250 - 5330 @ 80), (30), DFS (5735 - 5835 @ 80), (30) @@ -1040,41 +881,25 @@ country NI: DFS-FCC (57240 - 63720 @ 2160), (40) country NL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country NO: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country NP: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (20) (5735 - 5835 @ 80), (20) @@ -1088,39 +913,41 @@ country NZ: DFS-FCC (57000 - 66000 @ 2160), (40) country OM: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country PA: (2402 - 2472 @ 40), (36) - (5170 - 5250 @ 80), (23), AUT0-BW - (5250 - 5330 @ 80), (30), AUTO-BW - (5735 - 5835 @ 80), (36) + (5170 - 5250 @ 80), (30), AUT0-BW + (5250 - 5330 @ 80), (24), AUTO-BW + (5490 - 5730 @ 160), (24), + (5735 - 5835 @ 80), (30) country PE: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country PF: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country PG: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country PH: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1133,27 +960,20 @@ country PK: (5735 - 5835 @ 80), (30) country PL: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country PM: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country PR: DFS-FCC (2402 - 2472 @ 40), (30) @@ -1170,19 +990,11 @@ country PS: DFS-FCC (4940 - 4990 @ 40), (33) country PT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) @@ -1194,7 +1006,7 @@ country PW: DFS-FCC (5735 - 5835 @ 80), (30) country PY: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1202,45 +1014,41 @@ country PY: DFS-FCC # 60 gHz band channels 1-3, FCC (57240 - 63720 @ 2160), (40) -country QA: +country QA: DFS-ETSI (2402 - 2482 @ 40), (20) - (5735 - 5835 @ 80), (30) + (5170 - 5250 @ 80), (23), AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5490 - 5730 @ 160), (20), DFS + (5735 - 5875 @ 80), (20) country RE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country RO: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) # Source: # http://www.ratel.rs/upload/documents/Plan_namene/Plan_namene-sl_glasnik.pdf country RS: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country RU: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (23) (5490 - 5730 @ 160), (30) (5735 - 5835 @ 80), (30) @@ -1248,37 +1056,30 @@ country RU: (57240 - 65880 @ 2160), (40) country RW: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country SA: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS + (5735 - 5835 @ 80), (30) country SE: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country SG: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1287,78 +1088,62 @@ country SG: DFS-FCC (57000 - 66000 @ 2160), (40), NO-OUTDOOR country SI: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country SK: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS - # 5.9ghz band - # reference: http://www.etsi.org/deliver/etsi_en/302500_302599/302571/01.02.00_20/en_302571v010200a.pdf - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57000 - 66000 @ 2160), (40) country SN: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5330 @ 160), (24) (5490 - 5730 @ 160), (24) (5735 - 5835 @ 80), (30) country SR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country SV: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 20), (23) (5250 - 5330 @ 20), (23), DFS (5735 - 5835 @ 20), (30) country TC: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country TD: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country TG: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 40), (23) (5250 - 5330 @ 40), (23), DFS (5490 - 5710 @ 40), (30), DFS country TH: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1367,21 +1152,22 @@ country TH: DFS-FCC (57240 - 65880 @ 2160), (40) country TN: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW country TR: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country TT: - (2402 - 2482 @ 40), (20) - (5170 - 5330 @ 160), (24) - (5490 - 5730 @ 160), (24) - (5735 - 5835 @ 80), (30) + (2402 - 2482 @ 40), (23) + (5170 - 5330 @ 160), (27) + (5490 - 5730 @ 160), (36) + (5735 - 5835 @ 80), (36) # 60 gHz band channels 1-3, FCC (57240 - 63720 @ 2160), (40) @@ -1395,7 +1181,7 @@ country TW: DFS-FCC (57240 - 63720 @ 2160), (40) country TZ: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5735 - 5835 @ 80), (30) # Source: @@ -1407,15 +1193,15 @@ country TZ: # disputable definitions there. country UA: DFS-ETSI (2402 - 2482 @ 40), (20) - (5170 - 5250 @ 80), (20), AUTO-BW - (5250 - 5330 @ 80), (20), DFS, AUTO-BW - (5490 - 5670 @ 160), (20), DFS - (5735 - 5835 @ 80), (20) + (5170 - 5250 @ 80), (23), AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5490 - 5710 @ 160), (24), DFS + (5735 - 5835 @ 80), (24) # 60 gHz band channels 1-4, ref: Etsi En 302 567 (57240 - 65880 @ 2160), (20) country UG: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1427,25 +1213,13 @@ country US: DFS-FCC (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) - # 5.9ghz band - # reference: https://apps.fcc.gov/edocs_public/attachmatch/FCC-03-324A1.pdf - (5842 - 5863 @ 5), (30) - (5850 - 5870 @ 10), (30) - (5860 - 5880 @ 10), (30) - (5865 - 5885 @ 20), (30) - (5870 - 5890 @ 10), (30) - (5880 - 5900 @ 10), (30) - (5890 - 5910 @ 10), (30) - (5895 - 5915 @ 20), (30) - (5900 - 5920 @ 10), (30) - (5910 - 5930 @ 10), (30) # 60g band # reference: http://cfr.regstoday.com/47cfr15.aspx#47_CFR_15p255 # channels 1,2,3,4,5,6 EIRP=40dBm(43dBm peak) (57240 - 70200 @ 2160), (40) country UY: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (30) @@ -1453,20 +1227,21 @@ country UY: DFS-FCC (57240 - 65880 @ 2160), (40) country UZ: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5250 - 5330 @ 80), (20), DFS, AUTO-BW country VC: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW - (5490 - 5710 @ 160), (30), DFS + (5490 - 5730 @ 160), (30), DFS + (5735 - 5875 @ 80), (14) country VE: DFS-FCC (2402 - 2482 @ 40), (30) (5170 - 5250 @ 80), (23), AUTO-BW - (5250 - 5330 @ 80), (23), DFS, AUTO-BW + (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5735 - 5835 @ 80), (30) country VI: DFS-FCC @@ -1477,7 +1252,7 @@ country VI: DFS-FCC (5735 - 5835 @ 80), (30) country VN: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24) (5250 - 5330 @ 80), (24), DFS (5490 - 5730 @ 80), (24), DFS @@ -1486,42 +1261,42 @@ country VN: DFS-FCC (57240 - 65880 @ 2160), (40) country VU: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS (5735 - 5835 @ 80), (30) country WF: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country WS: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 40), (23) (5250 - 5330 @ 40), (23), DFS (5490 - 5710 @ 40), (30), DFS country XA: DFS-JP - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (2474 - 2494 @ 20), (20), NO-OFDM (5170 - 5250 @ 80), (20), NO-IR, AUTO-BW, NO-OUTDOOR (5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR (5490 - 5710 @ 160), (20), DFS country YE: - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) country YT: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS country ZA: DFS-FCC - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (24), AUTO-BW (5250 - 5330 @ 80), (24), DFS, AUTO-BW (5490 - 5730 @ 160), (24), DFS @@ -1530,7 +1305,7 @@ country ZA: DFS-FCC (57240 - 65880 @ 2160), (40), NO-OUTDOOR country ZW: DFS-ETSI - (2402 - 2482 @ 40), (20) + (2402 - 2482 @ 40), (23) (5170 - 5250 @ 80), (23), AUTO-BW (5250 - 5330 @ 80), (23), DFS, AUTO-BW (5490 - 5710 @ 160), (30), DFS diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 02996be239bc..8665edb29588 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2556,15 +2556,15 @@ sub process { } # Check for added, moved or deleted files - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2))))) { - $reported_maintainer_file = 1; - WARN("FILE_PATH_CHANGES", - "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } +## if (!$reported_maintainer_file && !$in_commit_log && +## ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || +## $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || +## ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && +## (defined($1) || defined($2))))) { +## $reported_maintainer_file = 1; +## WARN("FILE_PATH_CHANGES", +## "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); +## } #check the patch for invalid author credentials if ($chk_author && !($line =~ /^From:.*qca\.qualcomm\.com/) && @@ -3658,9 +3658,11 @@ sub process { } # avoid LINUX_VERSION_CODE - if ($line =~ /\bLINUX_VERSION_CODE\b/) { - WARN("LINUX_VERSION_CODE", - "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); +# if ($line =~ /\bLINUX_VERSION_CODE\b/) { +# WARN("LINUX_VERSION_CODE", +# "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); +# } + } # check for uses of printk_ratelimit diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index c02da25d7b63..63eca1289224 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -128,6 +128,7 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1; #define SEL_INO_MASK 0x00ffffff #define TMPBUFLEN 12 + static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5305cc6071e8..957938377a27 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -425,5 +425,19 @@ obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o + +#wangdongdong@MultiMediaService,2016/09/30,add for tfa9891 +obj-y += snd-soc-tfa98xx.o + +snd-soc-tfa98xx-objs += tfa9891/tfa98xx.o +snd-soc-tfa98xx-objs += tfa9891/tfa_container.o +snd-soc-tfa98xx-objs += tfa9891/tfa_dsp.o +snd-soc-tfa98xx-objs += tfa9891/tfa9888_init.o +snd-soc-tfa98xx-objs += tfa9891/tfa9891_init.o +snd-soc-tfa98xx-objs += tfa9891/tfa9897_init.o +snd-soc-tfa98xx-objs += tfa9891/tfa9890_init.o +snd-soc-tfa98xx-objs += tfa9891/tfa9887B_init.o +snd-soc-tfa98xx-objs += tfa9891/tfa9887_init.o + obj-y += sdm660_cdc/ obj-y += msm_sdw/ diff --git a/sound/soc/codecs/tfa9891/config.h b/sound/soc/codecs/tfa9891/config.h new file mode 100755 index 000000000000..5c19f1239dce --- /dev/null +++ b/sound/soc/codecs/tfa9891/config.h @@ -0,0 +1,146 @@ +#ifndef __CONFIG_LINUX_KERNEL_INC__ +#define __CONFIG_LINUX_KERNEL_INC__ + +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) "%s:" fmt,__func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + i2c transaction on Linux limited to 64k + (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 + +/* max. length of a alsa mixer control name */ +#define MAX_CONTROL_NAME 48 + +/* dbgprint.h */ +#define PRINT(fmt) "%s: " fmt, __func__ + +#define _ASSERT(e) +#define PRINT_ASSERT(e)if ((e)) printk(KERN_ERR "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) + +#define TFA98XX_MAX_REGISTER 0xff + +#define TFA98XX_FLAG_DSP_START_ON_MUTE (1 << 0) +#define TFA98XX_FLAG_SKIP_INTERRUPTS (1 << 1) +#define TFA98XX_FLAG_SAAM_AVAILABLE (1 << 2) +#define TFA98XX_FLAG_STEREO_DEVICE (1 << 3) +#define TFA98XX_FLAG_MULTI_MIC_INPUTS (1 << 4) +#define TFA98XX_FLAG_TAPDET_AVAILABLE (1 << 5) +#define TFA98XX_FLAG_TFA9890_FAM_DEV (1 << 6) +#define TFA98XX_FLAG_TFA9897_FAM_DEV (1 << 7) + +#define TFA98XX_NUM_RATES 9 +/* DSP init status */ +enum tfa98xx_dsp_init_state { + TFA98XX_DSP_INIT_STOPPED, /* DSP not running */ + TFA98XX_DSP_INIT_RECOVER, /* DSP error detected at runtime */ + TFA98XX_DSP_INIT_FAIL, /* DSP init failed */ + TFA98XX_DSP_INIT_PENDING, /* DSP start requested */ + TFA98XX_DSP_INIT_DONE, /* DSP running */ + TFA98XX_DSP_INIT_INVALIDATED, /* DSP was running, requires re-init */ +}; + +enum tfa98xx_dsp_fw_state { + TFA98XX_DSP_FW_NONE = 0, + TFA98XX_DSP_FW_PENDING, + TFA98XX_DSP_FW_FAIL, + TFA98XX_DSP_FW_OK, +}; + +struct tfa98xx_firmware { + void *base; + struct tfa98xx_device *dev; + char name[9]; //TODO get length from tfa parameter defs +}; + +struct tfa98xx_baseprofile { + char basename[MAX_CONTROL_NAME]; /* profile basename */ + int len; /* profile length */ + int item_id; /* profile id */ + int sr_rate_sup[TFA98XX_NUM_RATES]; /* sample rates supported by this profile */ + struct list_head list; /* list of all profiles */ +}; + +struct tfa98xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct regulator *vdd; + struct snd_soc_codec *codec; + struct workqueue_struct *tfa98xx_wq; + struct delayed_work init_work; + struct delayed_work monitor_work; + struct delayed_work interrupt_work; + struct delayed_work tapdet_work; + struct mutex dsp_lock; + int dsp_init; + int dsp_fw_state; + int sysclk; + int rst_gpio; + u16 rev; + int has_drc; + int audio_mode; + struct tfa98xx_firmware fw; + char *fw_name; + int rate; + wait_queue_head_t wq; + struct device *dev; + unsigned int init_count; + int pstream; + int cstream; + struct input_dev *input; + bool tapdet_enabled; /* service enabled */ + bool tapdet_open; /* device file opened */ + unsigned int tapdet_profiles; /* tapdet profile bitfield */ + bool tapdet_poll; /* tapdet running on polling mode */ + + unsigned int rate_constraint_list[TFA98XX_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; + + int reset_gpio; + int power_gpio; + int irq_gpio; + + int handle; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + u8 reg; + +//su + bool startInit; + + unsigned int count_wait_for_source_state; + unsigned int count_noclk; + unsigned int flags; +}; + +#if defined(CONFIG_TRACING) && defined(DEBUG) + #define tfa98xx_trace_printk(...) trace_printk(__VA_ARGS__) +#else + #define tfa98xx_trace_printk(...) +#endif + +/* + i2c transaction on Linux limited to 64k + (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +static inline int NXP_I2C_BufferSize(void) +{ + return 65536; +} + +#endif /* __CONFIG_LINUX_KERNEL_INC__ */ + diff --git a/sound/soc/codecs/tfa9891/tfa.h b/sound/soc/codecs/tfa9891/tfa.h new file mode 100755 index 000000000000..02ee52c0d3eb --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa.h @@ -0,0 +1,117 @@ +/* + *Copyright 2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#ifndef TFA_H_ +#define TFA_H_ + +/* set the limit for the container file length */ +#define TFA_MAX_CNT_LENGTH (256*1024) + +/** + * tfa error returns + */ +enum tfa_error { + tfa_error_ok, /**< no error */ + tfa_error_device, /**< no response from device */ + tfa_error_bad_param, /**< parameter no accepted */ + tfa_error_noclock, /**< required clock not present */ + tfa_error_timeout, /**< a timeout occurred */ + tfa_error_dsp, /**< a DSP error was returned */ + tfa_error_container, /**< no or wrong container file */ + tfa_error_max /**< impossible value, max enum */ +}; + +/** + * Pass the container buffer, initialize and allocate internal memory. + * + * Note that this buffer will be kept and should not be freed until + * tfa_deinit() has been called + * + * @param pointer to the start of the buffer holding the container file + * @param length of the data in bytes + * @return + * - tfa_error_ok if normal + * - tfa_error_container invalid container data + * - tfa_error_bad_param invalid parameter + * + */ +enum tfa_error tfa_load_cnt(void *cnt, int length); + +/** + * Probe/init the device. + * + * This function should only be called when the container file is loaded. + * It checks if an device with slave address is available in the container file. + * When a container file is available its assigns an handle matching the index + * in the container file and otherwise it will assign the first handle. + * It will check the device type and fill the device specific structures and + * functions. + * + * @param slave_address i2c slave address (8 bit format) + * @param pDevice the index in the conainer file + * @return enum tfa_error + */ +enum Tfa98xx_Error +tfa_probe(unsigned char slave_address, int *pDevice); + +/** + * Start/Restart the SpeakerBoost on all devices/channels. + * + * This should only be called when the audio input clock is active.\n + * When the device is in coldstart-state (ACS=1) then a full initialization + * will be performed.\n + * In case of a warm start only a power-on and un-mute will be executed.\n + * + * @param profile the profile to load, if -1 then don't change profile + * @param vsteps the volume step selections for each channel, if -1 then softmute + * 0 sets the maximum volume + * @return enum tfa_error + */ +enum tfa_error tfa_start(int profile, int *vstep); + +/** + * Stop SpeakerBoost on all devices/channels. + * + * This the notification of the audio clock to be taken away by the host. + * + * Note that the function will block until the amplifiers are actually switched + * off unless timed-out. + * + * @return enum tfa_error + */ +enum tfa_error tfa_stop(void); + +/** + * discard container buffer and free all resources.\n + * This includes discarding all callbacks. + */ +void tfa_deinit(void); + +/** + * bring all devices/channels in the cold state (ACS==1).\n + * This will cause reloading of all data at the next start + * + * @return + * - tfa_error_ok if normal + * - tfa_error_container invalid container data + * - tfa_error_device channel error + * - tfa_error_noclock only register level init could be preformed + */ +enum tfa_error tfa_reset(void); + +enum Tfa98xx_Error tfa_write_filters(int dev_idx, int prof_idx); + +#endif /* TFA_H_ */ diff --git a/sound/soc/codecs/tfa9891/tfa1_tfafieldnames.h b/sound/soc/codecs/tfa9891/tfa1_tfafieldnames.h new file mode 100755 index 000000000000..6cafe7d3f195 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa1_tfafieldnames.h @@ -0,0 +1,900 @@ +/** Filename: Tfa1_TfaFieldnames.h + * This file was generated automatically on 03/20/2015 at 01:55:46 PM. + * Source file: TFA9897N1B_I2C_list_URT_Source_v34.xls + */ + +typedef enum nxpTfa1BfEnumList { + TFA1_BF_VDDS = 0x0000, /*!< Power-on-reset flag */ + TFA1_BF_PLLS = 0x0010, /*!< PLL lock */ + TFA1_BF_OTDS = 0x0020, /*!< Over Temperature Protection alarm */ + TFA1_BF_OVDS = 0x0030, /*!< Over Voltage Protection alarm */ + TFA1_BF_UVDS = 0x0040, /*!< Under Voltage Protection alarm */ + TFA1_BF_OCDS = 0x0050, /*!< Over Current Protection alarm */ + TFA1_BF_CLKS = 0x0060, /*!< Clocks stable flag */ + TFA1_BF_CLIPS = 0x0070, /*!< Amplifier clipping */ + TFA1_BF_MTPB = 0x0080, /*!< MTP busy */ + TFA1_BF_NOCLK = 0x0090, /*!< Flag lost clock from clock generation unit */ + TFA1_BF_SPKS = 0x00a0, /*!< Speaker error flag */ + TFA1_BF_ACS = 0x00b0, /*!< Cold Start flag */ + TFA1_BF_SWS = 0x00c0, /*!< Flag Engage */ + TFA1_BF_WDS = 0x00d0, /*!< Flag watchdog reset */ + TFA1_BF_AMPS = 0x00e0, /*!< Amplifier is enabled by manager */ + TFA1_BF_AREFS = 0x00f0, /*!< References are enabled by manager */ + TFA1_BF_BATS = 0x0109, /*!< Battery voltage readout; 0 .. 5.5 [V] */ + TFA1_BF_TEMPS = 0x0208, /*!< Temperature readout from the temperature sensor */ + TFA1_BF_REV = 0x030b, /*!< Device type number is B97 */ + TFA1_BF_RCV = 0x0420, /*!< Enable Receiver Mode */ + TFA1_BF_CHS12 = 0x0431, /*!< Channel Selection TDM input for Coolflux */ + TFA1_BF_INPLVL= 0x0450, /*!< Input level selection control */ + TFA1_BF_CHSA = 0x0461, /*!< Input selection for amplifier */ + TFA1_BF_I2SDOE= 0x04b0, /*!< Enable data output */ + TFA1_BF_AUDFS = 0x04c3, /*!< Audio sample rate setting */ + TFA1_BF_BSSCR = 0x0501, /*!< Protection Attack Time */ + TFA1_BF_BSST = 0x0523, /*!< ProtectionThreshold */ + TFA1_BF_BSSRL = 0x0561, /*!< Protection Maximum Reduction */ + TFA1_BF_BSSRR = 0x0582, /*!< Battery Protection Release Time */ + TFA1_BF_BSSHY = 0x05b1, /*!< Battery Protection Hysteresis */ + TFA1_BF_BSSR = 0x05e0, /*!< battery voltage for I2C read out only */ + TFA1_BF_BSSBY = 0x05f0, /*!< bypass clipper battery protection */ + TFA1_BF_DPSA = 0x0600, /*!< Enable dynamic powerstage activation */ + TFA1_BF_CFSM = 0x0650, /*!< Soft mute in CoolFlux */ + TFA1_BF_BSSS = 0x0670, /*!< BatSenseSteepness */ + TFA1_BF_VOL = 0x0687, /*!< volume control (in CoolFlux) */ + TFA1_BF_DCVO = 0x0702, /*!< Boost Voltage */ + TFA1_BF_DCMCC = 0x0733, /*!< Max boost coil current - step of 175 mA */ + TFA1_BF_DCIE = 0x07a0, /*!< Adaptive boost mode */ + TFA1_BF_DCSR = 0x07b0, /*!< Soft RampUp/Down mode for DCDC controller */ + TFA1_BF_DCPAVG= 0x07c0, /*!< ctrl_peak2avg for analog part of DCDC */ + TFA1_BF_TROS = 0x0800, /*!< Select external temperature also the ext_temp will be put on the temp read out */ + TFA1_BF_EXTTS = 0x0818, /*!< external temperature setting to be given by host */ + TFA1_BF_PWDN = 0x0900, /*!< Device Mode */ + TFA1_BF_I2CR = 0x0910, /*!< I2C Reset */ + TFA1_BF_CFE = 0x0920, /*!< Enable CoolFlux */ + TFA1_BF_AMPE = 0x0930, /*!< Enable Amplifier */ + TFA1_BF_DCA = 0x0940, /*!< EnableBoost */ + TFA1_BF_SBSL = 0x0950, /*!< Coolflux configured */ + TFA1_BF_AMPC = 0x0960, /*!< Selection on how Amplifier is enabled */ + TFA1_BF_DCDIS = 0x0970, /*!< DCDC not connected */ + TFA1_BF_PSDR = 0x0980, /*!< IDDQ test amplifier */ + TFA1_BF_DCCV = 0x0991, /*!< Coil Value */ + TFA1_BF_CCFD = 0x09b0, /*!< Selection CoolFlux Clock */ + TFA1_BF_INTPAD= 0x09c1, /*!< INT pad configuration control */ + TFA1_BF_IPLL = 0x09e0, /*!< PLL input reference clock selection */ + TFA1_BF_MTPK = 0x0b07, /*!< 5Ah, 90d To access KEY1_Protected registers (Default for engineering) */ + TFA1_BF_CVFDLY= 0x0c25, /*!< Fractional delay adjustment between current and voltage sense */ + TFA1_BF_TDMPRF= 0x1011, /*!< TDM_usecase */ + TFA1_BF_TDMEN = 0x1030, /*!< TDM interface control */ + TFA1_BF_TDMCKINV= 0x1040, /*!< TDM clock inversion */ + TFA1_BF_TDMFSLN= 0x1053, /*!< TDM FS length */ + TFA1_BF_TDMFSPOL= 0x1090, /*!< TDM FS polarity */ + TFA1_BF_TDMSAMSZ= 0x10a4, /*!< TDM Sample Size for all tdm sinks/sources */ + TFA1_BF_TDMSLOTS= 0x1103, /*!< Number of slots */ + TFA1_BF_TDMSLLN= 0x1144, /*!< Slot length */ + TFA1_BF_TDMBRMG= 0x1194, /*!< Bits remaining */ + TFA1_BF_TDMDDEL= 0x11e0, /*!< Data delay */ + TFA1_BF_TDMDADJ= 0x11f0, /*!< Data adjustment */ + TFA1_BF_TDMTXFRM= 0x1201, /*!< TXDATA format */ + TFA1_BF_TDMUUS0= 0x1221, /*!< TXDATA format unused slot sd0 */ + TFA1_BF_TDMUUS1= 0x1241, /*!< TXDATA format unused slot sd1 */ + TFA1_BF_TDMSI0EN= 0x1270, /*!< TDM sink0 enable */ + TFA1_BF_TDMSI1EN= 0x1280, /*!< TDM sink1 enable */ + TFA1_BF_TDMSI2EN= 0x1290, /*!< TDM sink2 enable */ + TFA1_BF_TDMSO0EN= 0x12a0, /*!< TDM source0 enable */ + TFA1_BF_TDMSO1EN= 0x12b0, /*!< TDM source1 enable */ + TFA1_BF_TDMSO2EN= 0x12c0, /*!< TDM source2 enable */ + TFA1_BF_TDMSI0IO= 0x12d0, /*!< tdm_sink0_io */ + TFA1_BF_TDMSI1IO= 0x12e0, /*!< tdm_sink1_io */ + TFA1_BF_TDMSI2IO= 0x12f0, /*!< tdm_sink2_io */ + TFA1_BF_TDMSO0IO= 0x1300, /*!< tdm_source0_io */ + TFA1_BF_TDMSO1IO= 0x1310, /*!< tdm_source1_io */ + TFA1_BF_TDMSO2IO= 0x1320, /*!< tdm_source2_io */ + TFA1_BF_TDMSI0SL= 0x1333, /*!< sink0_slot [GAIN IN] */ + TFA1_BF_TDMSI1SL= 0x1373, /*!< sink1_slot [CH1 IN] */ + TFA1_BF_TDMSI2SL= 0x13b3, /*!< sink2_slot [CH2 IN] */ + TFA1_BF_TDMSO0SL= 0x1403, /*!< source0_slot [GAIN OUT] */ + TFA1_BF_TDMSO1SL= 0x1443, /*!< source1_slot [Voltage Sense] */ + TFA1_BF_TDMSO2SL= 0x1483, /*!< source2_slot [Current Sense] */ + TFA1_BF_NBCK = 0x14c3, /*!< NBCK */ + TFA1_BF_INTOVDDS= 0x2000, /*!< flag_por_int_out */ + TFA1_BF_INTOPLLS= 0x2010, /*!< flag_pll_lock_int_out */ + TFA1_BF_INTOOTDS= 0x2020, /*!< flag_otpok_int_out */ + TFA1_BF_INTOOVDS= 0x2030, /*!< flag_ovpok_int_out */ + TFA1_BF_INTOUVDS= 0x2040, /*!< flag_uvpok_int_out */ + TFA1_BF_INTOOCDS= 0x2050, /*!< flag_ocp_alarm_int_out */ + TFA1_BF_INTOCLKS= 0x2060, /*!< flag_clocks_stable_int_out */ + TFA1_BF_INTOCLIPS= 0x2070, /*!< flag_clip_int_out */ + TFA1_BF_INTOMTPB= 0x2080, /*!< mtp_busy_int_out */ + TFA1_BF_INTONOCLK= 0x2090, /*!< flag_lost_clk_int_out */ + TFA1_BF_INTOSPKS= 0x20a0, /*!< flag_cf_speakererror_int_out */ + TFA1_BF_INTOACS= 0x20b0, /*!< flag_cold_started_int_out */ + TFA1_BF_INTOSWS= 0x20c0, /*!< flag_engage_int_out */ + TFA1_BF_INTOWDS= 0x20d0, /*!< flag_watchdog_reset_int_out */ + TFA1_BF_INTOAMPS= 0x20e0, /*!< flag_enbl_amp_int_out */ + TFA1_BF_INTOAREFS= 0x20f0, /*!< flag_enbl_ref_int_out */ + TFA1_BF_INTOACK= 0x2201, /*!< Interrupt status register output - Corresponding flag */ + TFA1_BF_INTIVDDS= 0x2300, /*!< flag_por_int_in */ + TFA1_BF_INTIPLLS= 0x2310, /*!< flag_pll_lock_int_in */ + TFA1_BF_INTIOTDS= 0x2320, /*!< flag_otpok_int_in */ + TFA1_BF_INTIOVDS= 0x2330, /*!< flag_ovpok_int_in */ + TFA1_BF_INTIUVDS= 0x2340, /*!< flag_uvpok_int_in */ + TFA1_BF_INTIOCDS= 0x2350, /*!< flag_ocp_alarm_int_in */ + TFA1_BF_INTICLKS= 0x2360, /*!< flag_clocks_stable_int_in */ + TFA1_BF_INTICLIPS= 0x2370, /*!< flag_clip_int_in */ + TFA1_BF_INTIMTPB= 0x2380, /*!< mtp_busy_int_in */ + TFA1_BF_INTINOCLK= 0x2390, /*!< flag_lost_clk_int_in */ + TFA1_BF_INTISPKS= 0x23a0, /*!< flag_cf_speakererror_int_in */ + TFA1_BF_INTIACS= 0x23b0, /*!< flag_cold_started_int_in */ + TFA1_BF_INTISWS= 0x23c0, /*!< flag_engage_int_in */ + TFA1_BF_INTIWDS= 0x23d0, /*!< flag_watchdog_reset_int_in */ + TFA1_BF_INTIAMPS= 0x23e0, /*!< flag_enbl_amp_int_in */ + TFA1_BF_INTIAREFS= 0x23f0, /*!< flag_enbl_ref_int_in */ + TFA1_BF_INTIACK= 0x2501, /*!< Interrupt register input */ + TFA1_BF_INTENVDDS= 0x2600, /*!< flag_por_int_enable */ + TFA1_BF_INTENPLLS= 0x2610, /*!< flag_pll_lock_int_enable */ + TFA1_BF_INTENOTDS= 0x2620, /*!< flag_otpok_int_enable */ + TFA1_BF_INTENOVDS= 0x2630, /*!< flag_ovpok_int_enable */ + TFA1_BF_INTENUVDS= 0x2640, /*!< flag_uvpok_int_enable */ + TFA1_BF_INTENOCDS= 0x2650, /*!< flag_ocp_alarm_int_enable */ + TFA1_BF_INTENCLKS= 0x2660, /*!< flag_clocks_stable_int_enable */ + TFA1_BF_INTENCLIPS= 0x2670, /*!< flag_clip_int_enable */ + TFA1_BF_INTENMTPB= 0x2680, /*!< mtp_busy_int_enable */ + TFA1_BF_INTENNOCLK= 0x2690, /*!< flag_lost_clk_int_enable */ + TFA1_BF_INTENSPKS= 0x26a0, /*!< flag_cf_speakererror_int_enable */ + TFA1_BF_INTENACS= 0x26b0, /*!< flag_cold_started_int_enable */ + TFA1_BF_INTENSWS= 0x26c0, /*!< flag_engage_int_enable */ + TFA1_BF_INTENWDS= 0x26d0, /*!< flag_watchdog_reset_int_enable */ + TFA1_BF_INTENAMPS= 0x26e0, /*!< flag_enbl_amp_int_enable */ + TFA1_BF_INTENAREFS= 0x26f0, /*!< flag_enbl_ref_int_enable */ + TFA1_BF_INTENACK= 0x2801, /*!< Interrupt enable register */ + TFA1_BF_INTPOLVDDS= 0x2900, /*!< flag_por_int_pol */ + TFA1_BF_INTPOLPLLS= 0x2910, /*!< flag_pll_lock_int_pol */ + TFA1_BF_INTPOLOTDS= 0x2920, /*!< flag_otpok_int_pol */ + TFA1_BF_INTPOLOVDS= 0x2930, /*!< flag_ovpok_int_pol */ + TFA1_BF_INTPOLUVDS= 0x2940, /*!< flag_uvpok_int_pol */ + TFA1_BF_INTPOLOCDS= 0x2950, /*!< flag_ocp_alarm_int_pol */ + TFA1_BF_INTPOLCLKS= 0x2960, /*!< flag_clocks_stable_int_pol */ + TFA1_BF_INTPOLCLIPS= 0x2970, /*!< flag_clip_int_pol */ + TFA1_BF_INTPOLMTPB= 0x2980, /*!< mtp_busy_int_pol */ + TFA1_BF_INTPOLNOCLK= 0x2990, /*!< flag_lost_clk_int_pol */ + TFA1_BF_INTPOLSPKS= 0x29a0, /*!< flag_cf_speakererror_int_pol */ + TFA1_BF_INTPOLACS= 0x29b0, /*!< flag_cold_started_int_pol */ + TFA1_BF_INTPOLSWS= 0x29c0, /*!< flag_engage_int_pol */ + TFA1_BF_INTPOLWDS= 0x29d0, /*!< flag_watchdog_reset_int_pol */ + TFA1_BF_INTPOLAMPS= 0x29e0, /*!< flag_enbl_amp_int_pol */ + TFA1_BF_INTPOLAREFS= 0x29f0, /*!< flag_enbl_ref_int_pol */ + TFA1_BF_INTPOLACK= 0x2b01, /*!< Interrupt status flags polarity register */ + TFA1_BF_CLIP = 0x4900, /*!< Bypass clip control */ + TFA1_BF_CIMTP = 0x62b0, /*!< start copying all the data from i2cregs_mtp to mtp [Key 2 protected] */ + TFA1_BF_RST = 0x7000, /*!< Reset CoolFlux DSP */ + TFA1_BF_DMEM = 0x7011, /*!< Target memory for access */ + TFA1_BF_AIF = 0x7030, /*!< Autoincrement-flag for memory-address */ + TFA1_BF_CFINT = 0x7040, /*!< Interrupt CoolFlux DSP */ + TFA1_BF_REQ = 0x7087, /*!< request for access (8 channels) */ + TFA1_BF_REQCMD= 0x7080, /*!< Firmware event request rpc command */ + TFA1_BF_REQRST= 0x7090, /*!< Firmware event request reset restart */ + TFA1_BF_REQMIPS= 0x70a0, /*!< Firmware event request short on mips */ + TFA1_BF_REQMUTED= 0x70b0, /*!< Firmware event request mute sequence ready */ + TFA1_BF_REQVOL= 0x70c0, /*!< Firmware event request volume ready */ + TFA1_BF_REQDMG= 0x70d0, /*!< Firmware event request speaker damage detected */ + TFA1_BF_REQCAL= 0x70e0, /*!< Firmware event request calibration completed */ + TFA1_BF_REQRSV= 0x70f0, /*!< Firmware event request reserved */ + TFA1_BF_MADD = 0x710f, /*!< memory-address to be accessed */ + TFA1_BF_MEMA = 0x720f, /*!< activate memory access (24- or 32-bits data is written/read to/from memory */ + TFA1_BF_ERR = 0x7307, /*!< Coolflux error flags */ + TFA1_BF_ACK = 0x7387, /*!< acknowledge of requests (8 channels) */ + TFA1_BF_MTPOTC= 0x8000, /*!< Calibration schedule (key2 protected) */ + TFA1_BF_MTPEX = 0x8010, /*!< (key2 protected) */ +} nxpTfa1BfEnumList_t; +#define TFA1_NAMETABLE static tfaBfName_t Tfa1DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* Power-on-reset flag , */\ + { 0x10, "PLLS"}, /* PLL lock , */\ + { 0x20, "OTDS"}, /* Over Temperature Protection alarm , */\ + { 0x30, "OVDS"}, /* Over Voltage Protection alarm , */\ + { 0x40, "UVDS"}, /* Under Voltage Protection alarm , */\ + { 0x50, "OCDS"}, /* Over Current Protection alarm , */\ + { 0x60, "CLKS"}, /* Clocks stable flag , */\ + { 0x70, "CLIPS"}, /* Amplifier clipping , */\ + { 0x80, "MTPB"}, /* MTP busy , */\ + { 0x90, "NOCLK"}, /* Flag lost clock from clock generation unit , */\ + { 0xa0, "SPKS"}, /* Speaker error flag , */\ + { 0xb0, "ACS"}, /* Cold Start flag , */\ + { 0xc0, "SWS"}, /* Flag Engage , */\ + { 0xd0, "WDS"}, /* Flag watchdog reset , */\ + { 0xe0, "AMPS"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* References are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage readout; 0 .. 5.5 [V] , */\ + { 0x208, "TEMPS"}, /* Temperature readout from the temperature sensor , */\ + { 0x30b, "REV"}, /* Device type number is B97 , */\ + { 0x420, "RCV"}, /* Enable Receiver Mode , */\ + { 0x431, "CHS12"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "INPLVL"}, /* Input level selection control , */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "AUDFS"}, /* Audio sample rate setting , */\ + { 0x501, "SSCR"}, /* Protection Attack Time , */\ + { 0x523, "SST"}, /* ProtectionThreshold , */\ + { 0x561, "SSRL"}, /* Protection Maximum Reduction , */\ + { 0x582, "SSRR"}, /* Battery Protection Release Time , */\ + { 0x5b1, "SSHY"}, /* Battery Protection Hysteresis , */\ + { 0x5e0, "SSR"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "SSBY"}, /* bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "SSS"}, /* BatSenseSteepness , */\ + { 0x687, "VOL"}, /* volume control (in CoolFlux) , */\ + { 0x702, "DCVO"}, /* Boost Voltage , */\ + { 0x733, "DCMCC"}, /* Max boost coil current - step of 175 mA , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c0, "DCPAVG"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x800, "TROS"}, /* Select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "EXTTS"}, /* external temperature setting to be given by host , */\ + { 0x900, "PWDN"}, /* Device Mode , */\ + { 0x910, "I2CR"}, /* I2C Reset , */\ + { 0x920, "CFE"}, /* Enable CoolFlux , */\ + { 0x930, "AMPE"}, /* Enable Amplifier , */\ + { 0x940, "DCA"}, /* EnableBoost , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection on how Amplifier is enabled , */\ + { 0x970, "DCDIS"}, /* DCDC not connected , */\ + { 0x980, "PSDR"}, /* IDDQ test amplifier , */\ + { 0x991, "DCCV"}, /* Coil Value , */\ + { 0x9b0, "CCFD"}, /* Selection CoolFlux Clock , */\ + { 0x9c1, "INTPAD"}, /* INT pad configuration control , */\ + { 0x9e0, "IPLL"}, /* PLL input reference clock selection , */\ + { 0xb07, "MTPK"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xc25, "CVFDLY"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0x1011, "TDMPRF"}, /* TDM_usecase , */\ + { 0x1030, "TDMEN"}, /* TDM interface control , */\ + { 0x1040, "TDMCKINV"}, /* TDM clock inversion , */\ + { 0x1053, "TDMFSLN"}, /* TDM FS length , */\ + { 0x1090, "TDMFSPOL"}, /* TDM FS polarity , */\ + { 0x10a4, "TDMSAMSZ"}, /* TDM Sample Size for all tdm sinks/sources , */\ + { 0x1103, "TDMSLOTS"}, /* Number of slots , */\ + { 0x1144, "TDMSLLN"}, /* Slot length , */\ + { 0x1194, "TDMBRMG"}, /* Bits remaining , */\ + { 0x11e0, "TDMDDEL"}, /* Data delay , */\ + { 0x11f0, "TDMDADJ"}, /* Data adjustment , */\ + { 0x1201, "TDMTXFRM"}, /* TXDATA format , */\ + { 0x1221, "TDMUUS0"}, /* TXDATA format unused slot sd0 , */\ + { 0x1241, "TDMUUS1"}, /* TXDATA format unused slot sd1 , */\ + { 0x1270, "TDMSI0EN"}, /* TDM sink0 enable , */\ + { 0x1280, "TDMSI1EN"}, /* TDM sink1 enable , */\ + { 0x1290, "TDMSI2EN"}, /* TDM sink2 enable , */\ + { 0x12a0, "TDMSO0EN"}, /* TDM source0 enable , */\ + { 0x12b0, "TDMSO1EN"}, /* TDM source1 enable , */\ + { 0x12c0, "TDMSO2EN"}, /* TDM source2 enable , */\ + { 0x12d0, "TDMSI0IO"}, /* tdm_sink0_io , */\ + { 0x12e0, "TDMSI1IO"}, /* tdm_sink1_io , */\ + { 0x12f0, "TDMSI2IO"}, /* tdm_sink2_io , */\ + { 0x1300, "TDMSO0IO"}, /* tdm_source0_io , */\ + { 0x1310, "TDMSO1IO"}, /* tdm_source1_io , */\ + { 0x1320, "TDMSO2IO"}, /* tdm_source2_io , */\ + { 0x1333, "TDMSI0SL"}, /* sink0_slot [GAIN IN] , */\ + { 0x1373, "TDMSI1SL"}, /* sink1_slot [CH1 IN] , */\ + { 0x13b3, "TDMSI2SL"}, /* sink2_slot [CH2 IN] , */\ + { 0x1403, "TDMSO0SL"}, /* source0_slot [GAIN OUT] , */\ + { 0x1443, "TDMSO1SL"}, /* source1_slot [Voltage Sense] , */\ + { 0x1483, "TDMSO2SL"}, /* source2_slot [Current Sense] , */\ + { 0x14c3, "NBCK"}, /* NBCK , */\ + { 0x2000, "INTOVDDS"}, /* flag_por_int_out , */\ + { 0x2010, "INTOPLLS"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "INTOOTDS"}, /* flag_otpok_int_out , */\ + { 0x2030, "INTOOVDS"}, /* flag_ovpok_int_out , */\ + { 0x2040, "INTOUVDS"}, /* flag_uvpok_int_out , */\ + { 0x2050, "INTOOCDS"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "INTOCLKS"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "INTOCLIPS"}, /* flag_clip_int_out , */\ + { 0x2080, "INTOMTPB"}, /* mtp_busy_int_out , */\ + { 0x2090, "INTONOCLK"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "INTOSPKS"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "INTOACS"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "INTOSWS"}, /* flag_engage_int_out , */\ + { 0x20d0, "INTOWDS"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "INTOAMPS"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "INTOAREFS"}, /* flag_enbl_ref_int_out , */\ + { 0x2201, "INTOACK"}, /* Interrupt status register output - Corresponding flag, */\ + { 0x2300, "INTIVDDS"}, /* flag_por_int_in , */\ + { 0x2310, "INTIPLLS"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "INTIOTDS"}, /* flag_otpok_int_in , */\ + { 0x2330, "INTIOVDS"}, /* flag_ovpok_int_in , */\ + { 0x2340, "INTIUVDS"}, /* flag_uvpok_int_in , */\ + { 0x2350, "INTIOCDS"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "INTICLKS"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "INTICLIPS"}, /* flag_clip_int_in , */\ + { 0x2380, "INTIMTPB"}, /* mtp_busy_int_in , */\ + { 0x2390, "INTINOCLK"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "INTISPKS"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "INTIACS"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "INTISWS"}, /* flag_engage_int_in , */\ + { 0x23d0, "INTIWDS"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "INTIAMPS"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "INTIAREFS"}, /* flag_enbl_ref_int_in , */\ + { 0x2501, "INTIACK"}, /* Interrupt register input , */\ + { 0x2600, "INTENVDDS"}, /* flag_por_int_enable , */\ + { 0x2610, "INTENPLLS"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "INTENOTDS"}, /* flag_otpok_int_enable , */\ + { 0x2630, "INTENOVDS"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "INTENUVDS"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "INTENOCDS"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "INTENCLKS"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "INTENCLIPS"}, /* flag_clip_int_enable , */\ + { 0x2680, "INTENMTPB"}, /* mtp_busy_int_enable , */\ + { 0x2690, "INTENNOCLK"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "INTENSPKS"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "INTENACS"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "INTENSWS"}, /* flag_engage_int_enable , */\ + { 0x26d0, "INTENWDS"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "INTENAMPS"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "INTENAREFS"}, /* flag_enbl_ref_int_enable , */\ + { 0x2801, "INTENACK"}, /* Interrupt enable register , */\ + { 0x2900, "INTPOLVDDS"}, /* flag_por_int_pol , */\ + { 0x2910, "INTPOLPLLS"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "INTPOLOTDS"}, /* flag_otpok_int_pol , */\ + { 0x2930, "INTPOLOVDS"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "INTPOLUVDS"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "INTPOLOCDS"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "INTPOLCLKS"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "INTPOLCLIPS"}, /* flag_clip_int_pol , */\ + { 0x2980, "INTPOLMTPB"}, /* mtp_busy_int_pol , */\ + { 0x2990, "INTPOLNOCLK"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "INTPOLSPKS"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "INTPOLACS"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "INTPOLSWS"}, /* flag_engage_int_pol , */\ + { 0x29d0, "INTPOLWDS"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "INTPOLAMPS"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "INTPOLAREFS"}, /* flag_enbl_ref_int_pol , */\ + { 0x2b01, "INTPOLACK"}, /* Interrupt status flags polarity register , */\ + { 0x4900, "CLIP"}, /* Bypass clip control , */\ + { 0x62b0, "CIMTP"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "CFINT"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "REQ"}, /* request for access (8 channels) , */\ + { 0x7080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x7090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x70a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x70b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x70c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x70d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x70e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x70f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x710f, "MADD"}, /* memory-address to be accessed , */\ + { 0x720f, "MEMA"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "ERR"}, /* Coolflux error flags , */\ + { 0x7387, "ACK"}, /* acknowledge of requests (8 channels) , */\ + { 0x7380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x7390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x73a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x73b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x73c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x73d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x73e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x73f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "MTPEX"}, /* (key2 protected) , */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA1_BITNAMETABLE static tfaBfName_t Tfa1BitNames[]= {\ + { 0x0, "flag_por"}, /* Power-on-reset flag , */\ + { 0x10, "flag_pll_lock"}, /* PLL lock , */\ + { 0x20, "flag_otpok"}, /* Over Temperature Protection alarm , */\ + { 0x30, "flag_ovpok"}, /* Over Voltage Protection alarm , */\ + { 0x40, "flag_uvpok"}, /* Under Voltage Protection alarm , */\ + { 0x50, "flag_ocp_alarm"}, /* Over Current Protection alarm , */\ + { 0x60, "flag_clocks_stable"}, /* Clocks stable flag , */\ + { 0x70, "flag_clip"}, /* Amplifier clipping , */\ + { 0x80, "mtp_busy"}, /* MTP busy , */\ + { 0x90, "flag_lost_clk"}, /* Flag lost clock from clock generation unit , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker error flag , */\ + { 0xb0, "flag_cold_started"}, /* Cold Start flag , */\ + { 0xc0, "flag_engage"}, /* Flag Engage , */\ + { 0xd0, "flag_watchdog_reset"}, /* Flag watchdog reset , */\ + { 0xe0, "flag_enbl_amp"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* References are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage readout; 0 .. 5.5 [V] , */\ + { 0x208, "temp_adc"}, /* Temperature readout from the temperature sensor , */\ + { 0x30b, "rev_reg"}, /* Device type number is B97 , */\ + { 0x420, "ctrl_rcv"}, /* Enable Receiver Mode , */\ + { 0x431, "chan_sel"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "input_level"}, /* Input level selection control , */\ + { 0x461, "vamp_sel"}, /* Input selection for amplifier , */\ + { 0x4c3, "audio_fs"}, /* Audio sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* Protection Attack Time , */\ + { 0x523, "vbat_prot_thlevel"}, /* ProtectionThreshold , */\ + { 0x561, "vbat_prot_max_reduct"}, /* Protection Maximum Reduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Battery Protection Release Time , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* Battery Protection Hysteresis , */\ + { 0x5d0, "reset_min_vbat"}, /* reset clipper , */\ + { 0x5e0, "sel_vbat"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "bypass_clipper"}, /* bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x670, "batsense_steepness"}, /* BatSenseSteepness , */\ + { 0x687, "vol"}, /* volume control (in CoolFlux) , */\ + { 0x702, "boost_volt"}, /* Boost Voltage , */\ + { 0x733, "boost_cur"}, /* Max boost coil current - step of 175 mA , */\ + { 0x7a0, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7b0, "boost_speed"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c0, "boost_peak2avg"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x800, "ext_temp_sel"}, /* Select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "ext_temp"}, /* external temperature setting to be given by host , */\ + { 0x8b2, "dcdc_synchronisation"}, /* DCDC synchronisation off + 7 positions , */\ + { 0x900, "powerdown"}, /* Device Mode , */\ + { 0x910, "reset"}, /* I2C Reset , */\ + { 0x920, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x940, "enbl_boost"}, /* EnableBoost , */\ + { 0x950, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection on how Amplifier is enabled , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC not connected , */\ + { 0x980, "iddqtest"}, /* IDDQ test amplifier , */\ + { 0x991, "coil_value"}, /* Coil Value , */\ + { 0x9b0, "sel_cf_clock"}, /* Selection CoolFlux Clock , */\ + { 0x9c1, "int_pad_io"}, /* INT pad configuration control , */\ + { 0x9e0, "sel_fs_bck"}, /* PLL input reference clock selection , */\ + { 0x9f0, "sel_scl_cf_clock"}, /* Coolflux sub-system clock , */\ + { 0xb07, "mtpkey2"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xc00, "enbl_volt_sense"}, /* Voltage sense enabling control bit , */\ + { 0xc10, "vsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0xc25, "vi_frac_delay"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xc80, "sel_voltsense_out"}, /* TDM output data selection control , */\ + { 0xc90, "vsense_bypass_avg"}, /* Voltage Sense Average Block Bypass , */\ + { 0xd05, "cf_frac_delay"}, /* Fractional delay adjustment between current and voltage sense by firmware, */\ + { 0xe00, "bypass_dcdc_curr_prot"}, /* Control to switch off dcdc current reduction with bat protection, */\ + { 0xe80, "disable_clock_sh_prot"}, /* disable clock_sh protection , */\ + { 0xe96, "reserve_reg_1_15_9"}, /* , */\ + { 0x1011, "tdm_usecase"}, /* TDM_usecase , */\ + { 0x1030, "tdm_enable"}, /* TDM interface control , */\ + { 0x1040, "tdm_clk_inversion"}, /* TDM clock inversion , */\ + { 0x1053, "tdm_fs_ws_length"}, /* TDM FS length , */\ + { 0x1090, "tdm_fs_ws_polarity"}, /* TDM FS polarity , */\ + { 0x10a4, "tdm_sample_size"}, /* TDM Sample Size for all tdm sinks/sources , */\ + { 0x1103, "tdm_nb_of_slots"}, /* Number of slots , */\ + { 0x1144, "tdm_slot_length"}, /* Slot length , */\ + { 0x1194, "tdm_bits_remaining"}, /* Bits remaining , */\ + { 0x11e0, "tdm_data_delay"}, /* Data delay , */\ + { 0x11f0, "tdm_data_adjustment"}, /* Data adjustment , */\ + { 0x1201, "tdm_txdata_format"}, /* TXDATA format , */\ + { 0x1221, "tdm_txdata_format_unused_slot_sd0"}, /* TXDATA format unused slot sd0 , */\ + { 0x1241, "tdm_txdata_format_unused_slot_sd1"}, /* TXDATA format unused slot sd1 , */\ + { 0x1270, "tdm_sink0_enable"}, /* TDM sink0 enable , */\ + { 0x1280, "tdm_sink1_enable"}, /* TDM sink1 enable , */\ + { 0x1290, "tdm_sink2_enable"}, /* TDM sink2 enable , */\ + { 0x12a0, "tdm_source0_enable"}, /* TDM source0 enable , */\ + { 0x12b0, "tdm_source1_enable"}, /* TDM source1 enable , */\ + { 0x12c0, "tdm_source2_enable"}, /* TDM source2 enable , */\ + { 0x12d0, "tdm_sink0_io"}, /* tdm_sink0_io , */\ + { 0x12e0, "tdm_sink1_io"}, /* tdm_sink1_io , */\ + { 0x12f0, "tdm_sink2_io"}, /* tdm_sink2_io , */\ + { 0x1300, "tdm_source0_io"}, /* tdm_source0_io , */\ + { 0x1310, "tdm_source1_io"}, /* tdm_source1_io , */\ + { 0x1320, "tdm_source2_io"}, /* tdm_source2_io , */\ + { 0x1333, "tdm_sink0_slot"}, /* sink0_slot [GAIN IN] , */\ + { 0x1373, "tdm_sink1_slot"}, /* sink1_slot [CH1 IN] , */\ + { 0x13b3, "tdm_sink2_slot"}, /* sink2_slot [CH2 IN] , */\ + { 0x1403, "tdm_source0_slot"}, /* source0_slot [GAIN OUT] , */\ + { 0x1443, "tdm_source1_slot"}, /* source1_slot [Voltage Sense] , */\ + { 0x1483, "tdm_source2_slot"}, /* source2_slot [Current Sense] , */\ + { 0x14c3, "tdm_nbck"}, /* NBCK , */\ + { 0x1500, "flag_tdm_lut_error"}, /* TDM LUT error flag , */\ + { 0x1512, "flag_tdm_status"}, /* TDM interface status bits , */\ + { 0x1540, "flag_tdm_error"}, /* TDM interface error indicator , */\ + { 0x2000, "flag_por_int_out"}, /* flag_por_int_out , */\ + { 0x2010, "flag_pll_lock_int_out"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "flag_otpok_int_out"}, /* flag_otpok_int_out , */\ + { 0x2030, "flag_ovpok_int_out"}, /* flag_ovpok_int_out , */\ + { 0x2040, "flag_uvpok_int_out"}, /* flag_uvpok_int_out , */\ + { 0x2050, "flag_ocp_alarm_int_out"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "flag_clocks_stable_int_out"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "flag_clip_int_out"}, /* flag_clip_int_out , */\ + { 0x2080, "mtp_busy_int_out"}, /* mtp_busy_int_out , */\ + { 0x2090, "flag_lost_clk_int_out"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "flag_cf_speakererror_int_out"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "flag_cold_started_int_out"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "flag_engage_int_out"}, /* flag_engage_int_out , */\ + { 0x20d0, "flag_watchdog_reset_int_out"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "flag_enbl_amp_int_out"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "flag_enbl_ref_int_out"}, /* flag_enbl_ref_int_out , */\ + { 0x2100, "flag_voutcomp_int_out"}, /* flag_voutcomp_int_out , */\ + { 0x2110, "flag_voutcomp93_int_out"}, /* flag_voutcomp93_int_out , */\ + { 0x2120, "flag_voutcomp86_int_out"}, /* flag_voutcomp86_int_out , */\ + { 0x2130, "flag_hiz_int_out"}, /* flag_hiz_int_out , */\ + { 0x2140, "flag_ocpokbst_int_out"}, /* flag_ocpokbst_int_out , */\ + { 0x2150, "flag_peakcur_int_out"}, /* flag_peakcur_int_out , */\ + { 0x2160, "flag_ocpokap_int_out"}, /* flag_ocpokap_int_out , */\ + { 0x2170, "flag_ocpokan_int_out"}, /* flag_ocpokan_int_out , */\ + { 0x2180, "flag_ocpokbp_int_out"}, /* flag_ocpokbp_int_out , */\ + { 0x2190, "flag_ocpokbn_int_out"}, /* flag_ocpokbn_int_out , */\ + { 0x21a0, "flag_adc10_ready_int_out"}, /* flag_adc10_ready_int_out , */\ + { 0x21b0, "flag_clipa_high_int_out"}, /* flag_clipa_high_int_out , */\ + { 0x21c0, "flag_clipa_low_int_out"}, /* flag_clipa_low_int_out , */\ + { 0x21d0, "flag_clipb_high_int_out"}, /* flag_clipb_high_int_out , */\ + { 0x21e0, "flag_clipb_low_int_out"}, /* flag_clipb_low_int_out , */\ + { 0x21f0, "flag_tdm_error_int_out"}, /* flag_tdm_error_int_out , */\ + { 0x2201, "interrupt_out3"}, /* Interrupt status register output - Corresponding flag, */\ + { 0x2300, "flag_por_int_in"}, /* flag_por_int_in , */\ + { 0x2310, "flag_pll_lock_int_in"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "flag_otpok_int_in"}, /* flag_otpok_int_in , */\ + { 0x2330, "flag_ovpok_int_in"}, /* flag_ovpok_int_in , */\ + { 0x2340, "flag_uvpok_int_in"}, /* flag_uvpok_int_in , */\ + { 0x2350, "flag_ocp_alarm_int_in"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "flag_clocks_stable_int_in"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "flag_clip_int_in"}, /* flag_clip_int_in , */\ + { 0x2380, "mtp_busy_int_in"}, /* mtp_busy_int_in , */\ + { 0x2390, "flag_lost_clk_int_in"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "flag_cf_speakererror_int_in"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "flag_cold_started_int_in"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "flag_engage_int_in"}, /* flag_engage_int_in , */\ + { 0x23d0, "flag_watchdog_reset_int_in"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "flag_enbl_amp_int_in"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "flag_enbl_ref_int_in"}, /* flag_enbl_ref_int_in , */\ + { 0x2400, "flag_voutcomp_int_in"}, /* flag_voutcomp_int_in , */\ + { 0x2410, "flag_voutcomp93_int_in"}, /* flag_voutcomp93_int_in , */\ + { 0x2420, "flag_voutcomp86_int_in"}, /* flag_voutcomp86_int_in , */\ + { 0x2430, "flag_hiz_int_in"}, /* flag_hiz_int_in , */\ + { 0x2440, "flag_ocpokbst_int_in"}, /* flag_ocpokbst_int_in , */\ + { 0x2450, "flag_peakcur_int_in"}, /* flag_peakcur_int_in , */\ + { 0x2460, "flag_ocpokap_int_in"}, /* flag_ocpokap_int_in , */\ + { 0x2470, "flag_ocpokan_int_in"}, /* flag_ocpokan_int_in , */\ + { 0x2480, "flag_ocpokbp_int_in"}, /* flag_ocpokbp_int_in , */\ + { 0x2490, "flag_ocpokbn_int_in"}, /* flag_ocpokbn_int_in , */\ + { 0x24a0, "flag_adc10_ready_int_in"}, /* flag_adc10_ready_int_in , */\ + { 0x24b0, "flag_clipa_high_int_in"}, /* flag_clipa_high_int_in , */\ + { 0x24c0, "flag_clipa_low_int_in"}, /* flag_clipa_low_int_in , */\ + { 0x24d0, "flag_clipb_high_int_in"}, /* flag_clipb_high_int_in , */\ + { 0x24e0, "flag_clipb_low_int_in"}, /* flag_clipb_low_int_in , */\ + { 0x24f0, "flag_tdm_error_int_in"}, /* flag_tdm_error_int_in , */\ + { 0x2501, "interrupt_in3"}, /* Interrupt register input , */\ + { 0x2600, "flag_por_int_enable"}, /* flag_por_int_enable , */\ + { 0x2610, "flag_pll_lock_int_enable"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "flag_otpok_int_enable"}, /* flag_otpok_int_enable , */\ + { 0x2630, "flag_ovpok_int_enable"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "flag_uvpok_int_enable"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "flag_ocp_alarm_int_enable"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "flag_clocks_stable_int_enable"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "flag_clip_int_enable"}, /* flag_clip_int_enable , */\ + { 0x2680, "mtp_busy_int_enable"}, /* mtp_busy_int_enable , */\ + { 0x2690, "flag_lost_clk_int_enable"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "flag_cf_speakererror_int_enable"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "flag_cold_started_int_enable"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "flag_engage_int_enable"}, /* flag_engage_int_enable , */\ + { 0x26d0, "flag_watchdog_reset_int_enable"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "flag_enbl_amp_int_enable"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "flag_enbl_ref_int_enable"}, /* flag_enbl_ref_int_enable , */\ + { 0x2700, "flag_voutcomp_int_enable"}, /* flag_voutcomp_int_enable , */\ + { 0x2710, "flag_voutcomp93_int_enable"}, /* flag_voutcomp93_int_enable , */\ + { 0x2720, "flag_voutcomp86_int_enable"}, /* flag_voutcomp86_int_enable , */\ + { 0x2730, "flag_hiz_int_enable"}, /* flag_hiz_int_enable , */\ + { 0x2740, "flag_ocpokbst_int_enable"}, /* flag_ocpokbst_int_enable , */\ + { 0x2750, "flag_peakcur_int_enable"}, /* flag_peakcur_int_enable , */\ + { 0x2760, "flag_ocpokap_int_enable"}, /* flag_ocpokap_int_enable , */\ + { 0x2770, "flag_ocpokan_int_enable"}, /* flag_ocpokan_int_enable , */\ + { 0x2780, "flag_ocpokbp_int_enable"}, /* flag_ocpokbp_int_enable , */\ + { 0x2790, "flag_ocpokbn_int_enable"}, /* flag_ocpokbn_int_enable , */\ + { 0x27a0, "flag_adc10_ready_int_enable"}, /* flag_adc10_ready_int_enable , */\ + { 0x27b0, "flag_clipa_high_int_enable"}, /* flag_clipa_high_int_enable , */\ + { 0x27c0, "flag_clipa_low_int_enable"}, /* flag_clipa_low_int_enable , */\ + { 0x27d0, "flag_clipb_high_int_enable"}, /* flag_clipb_high_int_enable , */\ + { 0x27e0, "flag_clipb_low_int_enable"}, /* flag_clipb_low_int_enable , */\ + { 0x27f0, "flag_tdm_error_int_enable"}, /* flag_tdm_error_int_enable , */\ + { 0x2801, "interrupt_enable3"}, /* Interrupt enable register , */\ + { 0x2900, "flag_por_int_pol"}, /* flag_por_int_pol , */\ + { 0x2910, "flag_pll_lock_int_pol"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "flag_otpok_int_pol"}, /* flag_otpok_int_pol , */\ + { 0x2930, "flag_ovpok_int_pol"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "flag_uvpok_int_pol"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "flag_ocp_alarm_int_pol"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "flag_clocks_stable_int_pol"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "flag_clip_int_pol"}, /* flag_clip_int_pol , */\ + { 0x2980, "mtp_busy_int_pol"}, /* mtp_busy_int_pol , */\ + { 0x2990, "flag_lost_clk_int_pol"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "flag_cf_speakererror_int_pol"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "flag_cold_started_int_pol"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "flag_engage_int_pol"}, /* flag_engage_int_pol , */\ + { 0x29d0, "flag_watchdog_reset_int_pol"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "flag_enbl_amp_int_pol"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "flag_enbl_ref_int_pol"}, /* flag_enbl_ref_int_pol , */\ + { 0x2a00, "flag_voutcomp_int_pol"}, /* flag_voutcomp_int_pol , */\ + { 0x2a10, "flag_voutcomp93_int_pol"}, /* flag_voutcomp93_int_pol , */\ + { 0x2a20, "flag_voutcomp86_int_pol"}, /* flag_voutcomp86_int_pol , */\ + { 0x2a30, "flag_hiz_int_pol"}, /* flag_hiz_int_pol , */\ + { 0x2a40, "flag_ocpokbst_int_pol"}, /* flag_ocpokbst_int_pol , */\ + { 0x2a50, "flag_peakcur_int_pol"}, /* flag_peakcur_int_pol , */\ + { 0x2a60, "flag_ocpokap_int_pol"}, /* flag_ocpokap_int_pol , */\ + { 0x2a70, "flag_ocpokan_int_pol"}, /* flag_ocpokan_int_pol , */\ + { 0x2a80, "flag_ocpokbp_int_pol"}, /* flag_ocpokbp_int_pol , */\ + { 0x2a90, "flag_ocpokbn_int_pol"}, /* flag_ocpokbn_int_pol , */\ + { 0x2aa0, "flag_adc10_ready_int_pol"}, /* flag_adc10_ready_int_pol , */\ + { 0x2ab0, "flag_clipa_high_int_pol"}, /* flag_clipa_high_int_pol , */\ + { 0x2ac0, "flag_clipa_low_int_pol"}, /* flag_clipa_low_int_pol , */\ + { 0x2ad0, "flag_clipb_high_int_pol"}, /* flag_clipb_high_int_pol , */\ + { 0x2ae0, "flag_clipb_low_int_pol"}, /* flag_clipb_low_int_pol , */\ + { 0x2af0, "flag_tdm_error_int_pol"}, /* flag_tdm_error_int_pol , */\ + { 0x2b01, "status_polarity3"}, /* Interrupt status flags polarity register , */\ + { 0x3000, "flag_voutcomp"}, /* flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3010, "flag_voutcomp93"}, /* flag_voutcomp93, indication Vset is larger than 1.07* Vbat, */\ + { 0x3020, "flag_voutcomp86"}, /* flag_voutcomp86, indication Vset is larger than 1.14* Vbat, */\ + { 0x3030, "flag_hiz"}, /* flag_hiz, indication Vbst is larger than Vbat , */\ + { 0x3040, "flag_ocpokbst"}, /* flag_ocpokbst, indication no over current in boost converter pmos switch, */\ + { 0x3050, "flag_peakcur"}, /* flag_peakcur, indication current is max in dcdc converter, */\ + { 0x3060, "flag_ocpokap"}, /* flag_ocpokap, indication no over current in amplifier "a" pmos output stage, */\ + { 0x3070, "flag_ocpokan"}, /* flag_ocpokan, indication no over current in amplifier "a" nmos output stage, */\ + { 0x3080, "flag_ocpokbp"}, /* flag_ocpokbp, indication no over current in amplifier "b" pmos output stage, */\ + { 0x3090, "flag_ocpokbn"}, /* flag_ocpokbn, indication no over current in amplifier"b" nmos output stage, */\ + { 0x30a0, "flag_adc10_ready"}, /* flag_adc10_ready, indication adc10 is ready , */\ + { 0x30b0, "flag_clipa_high"}, /* flag_clipa_high, indication pmos amplifier "a" is clipping, */\ + { 0x30c0, "flag_clipa_low"}, /* flag_clipa_low, indication nmos amplifier "a" is clipping, */\ + { 0x30d0, "flag_clipb_high"}, /* flag_clipb_high, indication pmos amplifier "b" is clipping, */\ + { 0x30e0, "flag_clipb_low"}, /* flag_clipb_low, indication nmos amplifier "b" is clipping, */\ + { 0x310f, "mtp_man_data_out"}, /* single word read from MTP (manual copy) , */\ + { 0x3200, "key01_locked"}, /* key01_locked, indication key 1 is locked , */\ + { 0x3210, "key02_locked"}, /* key02_locked, indication key 2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* mtp_ecc_tcout , */\ + { 0x3280, "mtpctrl_valid_test_rd"}, /* mtp test readout for read , */\ + { 0x3290, "mtpctrl_valid_test_wr"}, /* mtp test readout for write , */\ + { 0x32a0, "flag_in_alarm_state"}, /* Alarm state , */\ + { 0x32b0, "mtp_ecc_err2"}, /* two or more bit errors detected in MTP, can not reconstruct value, */\ + { 0x32c0, "mtp_ecc_err1"}, /* one bit error detected in MTP, reconstructed value, */\ + { 0x32d0, "mtp_mtp_hvf"}, /* high voltage ready flag for MTP , */\ + { 0x32f0, "mtp_zero_check_fail"}, /* zero check failed (tbd) for MTP , */\ + { 0x3309, "data_adc10_tempbat"}, /* data_adc10_tempbat[9;0], adc 10 data output for testing, */\ + { 0x400f, "hid_code"}, /* 5A6Bh, 23147d to access registers (Default for engineering), */\ + { 0x4100, "bypass_hp"}, /* Bypass_High Pass Filter , */\ + { 0x4110, "hard_mute"}, /* Hard Mute , */\ + { 0x4120, "soft_mute"}, /* Soft Mute , */\ + { 0x4134, "pwm_delay"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "pwm_shape"}, /* PWM Shape , */\ + { 0x4190, "pwm_bitlength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4203, "drive"}, /* Drive bits to select amount of power stage amplifier, */\ + { 0x4240, "reclock_pwm"}, /* , */\ + { 0x4250, "reclock_voltsense"}, /* , */\ + { 0x4281, "dpsalevel"}, /* DPSA Threshold level , */\ + { 0x42a1, "dpsa_release"}, /* DPSA Release time , */\ + { 0x42c0, "coincidence"}, /* Prevent simultaneously switching of output stage , */\ + { 0x42d0, "kickback"}, /* Prevent double pulses of output stage , */\ + { 0x4306, "drivebst"}, /* Drive bits to select the powertransistor sections boost converter, */\ + { 0x43a0, "ocptestbst"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0);For new ocp (ctrl_reversebst is 1);, */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT coolflux , */\ + { 0x43f0, "test_bcontrol"}, /* test _bcontrol , */\ + { 0x4400, "reversebst"}, /* OverCurrent Protection selection of power stage boost converter, */\ + { 0x4410, "sensetest"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0x4420, "enbl_engagebst"}, /* Enable power stage dcdc controller , */\ + { 0x4470, "enbl_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x4480, "enbl_voutcomp"}, /* Enable vout comparators , */\ + { 0x4490, "enbl_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x44a0, "enbl_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x44b0, "enbl_hizcom"}, /* Enable hiz comparator , */\ + { 0x44c0, "enbl_peakcur"}, /* Enable peak current , */\ + { 0x44d0, "bypass_ovpglitch"}, /* Bypass OVP Glitch Filter , */\ + { 0x44e0, "enbl_windac"}, /* Enable window dac , */\ + { 0x44f0, "enbl_powerbst"}, /* Enable line of the powerstage , */\ + { 0x4507, "ocp_thr"}, /* ocp_thr threshold level for OCP , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45c0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* BypassOCPCounter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypasslost_clk detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0x4600, "bypass_gc"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* gain control by means of MTP or i2c; 0 is MTP , */\ + { 0x4627, "cs_gain"}, /* + / - 128 steps in steps of 1/4 percent 2's compliment, */\ + { 0x46a0, "bypass_lp"}, /* bypass Low-Pass filter in temperature sensor , */\ + { 0x46b0, "bypass_pwmcounter"}, /* bypass_pwmcounter , */\ + { 0x46c0, "cs_negfixed"}, /* does not switch to neg , */\ + { 0x46d2, "cs_neghyst"}, /* switches to neg depending on level , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x4800, "cs_negin"}, /* negin , */\ + { 0x4810, "cs_sein"}, /* cs_sein , */\ + { 0x4820, "cs_coincidence"}, /* Coincidence current sense , */\ + { 0x4830, "iddqtestbst"}, /* for iddq testing in powerstage of boost convertor , */\ + { 0x4840, "coincidencebst"}, /* Switch protection on to prevent simultaneously switching power stages bst and amp, */\ + { 0x4876, "delay_se_neg"}, /* delay of se and neg , */\ + { 0x48e1, "cs_ttrack"}, /* sample & hold track time , */\ + { 0x4900, "bypass_clip"}, /* Bypass clip control , */\ + { 0x4920, "cf_cgate_off"}, /* to disable clock gating in the coolflux , */\ + { 0x4940, "clipfast"}, /* clock switch for battery protection clipper, it switches back to old frequency, */\ + { 0x4950, "cs_8ohm"}, /* 8 ohm mode for current sense (gain mode) , */\ + { 0x4974, "delay_clock_sh"}, /* delay_sh, tunes S7H delay , */\ + { 0x49c0, "inv_clksh"}, /* Invert the sample/hold clock for current sense ADC, */\ + { 0x49d0, "inv_neg"}, /* Invert neg signal , */\ + { 0x49e0, "inv_se"}, /* Invert se signal , */\ + { 0x49f0, "setse"}, /* switches between Single Ende and differential mode; 1 is single ended, */\ + { 0x4a12, "adc10_sel"}, /* select the input to convert the 10b ADC , */\ + { 0x4a60, "adc10_reset"}, /* Global asynchronous reset (active HIGH) 10 bit ADC, */\ + { 0x4a81, "adc10_test"}, /* Test mode selection signal 10 bit ADC , */\ + { 0x4aa0, "bypass_lp_vbat"}, /* lp filter in batt sensor , */\ + { 0x4ae0, "dc_offset"}, /* switch offset control on/off, is decimator offset control, */\ + { 0x4af0, "tsense_hibias"}, /* bit to set the biasing in temp sensor to high , */\ + { 0x4b00, "adc13_iset"}, /* Micadc Setting of current consumption. Debug use only, */\ + { 0x4b14, "adc13_gain"}, /* Micadc gain setting (2-compl) , */\ + { 0x4b61, "adc13_slowdel"}, /* Micadc Delay setting for internal clock. Debug use only, */\ + { 0x4b83, "adc13_offset"}, /* Micadc ADC offset setting , */\ + { 0x4bc0, "adc13_bsoinv"}, /* Micadc bit stream output invert mode for test , */\ + { 0x4bd0, "adc13_resonator_enable"}, /* Micadc Give extra SNR with less stability. Debug use only, */\ + { 0x4be0, "testmicadc"}, /* Mux at input of MICADC for test purpose , */\ + { 0x4c0f, "abist_offset"}, /* offset control for ABIST testing , */\ + { 0x4d05, "windac"}, /* for testing direct control windac , */\ + { 0x4dc3, "pwm_dcc_cnt"}, /* control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0x4e04, "slopecur"}, /* for testing direct control slopecur , */\ + { 0x4e50, "ctrl_dem"}, /* dyn element matching control, rest of codes are optional, */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* to enable direct control of pwm duty cycle , */\ + { 0x5007, "gain"}, /* Gain setting of the gain multiplier , */\ + { 0x5081, "sourceb"}, /* Set OUTB to , */\ + { 0x50a1, "sourcea"}, /* Set OUTA to , */\ + { 0x50c1, "sourcebst"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0x50e0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0x5104, "pulselengthbst"}, /* pulse length setting test input for boost converter, */\ + { 0x5150, "bypasslatchbst"}, /* bypass_latch in boost converter , */\ + { 0x5160, "invertbst"}, /* invert pwmbst test signal , */\ + { 0x5174, "pulselength"}, /* pulse length setting test input for amplifier , */\ + { 0x51c0, "bypasslatch"}, /* bypass_latch in PWM source selection module , */\ + { 0x51d0, "invertb"}, /* invert pwmb test signal , */\ + { 0x51e0, "inverta"}, /* invert pwma test signal , */\ + { 0x51f0, "bypass_ctrlloop"}, /* bypass_ctrlloop bypasses the control loop of the amplifier, */\ + { 0x5210, "test_rdsona"}, /* tbd for rdson testing , */\ + { 0x5220, "test_rdsonb"}, /* tbd for rdson testing , */\ + { 0x5230, "test_rdsonbst"}, /* tbd for rdson testing , */\ + { 0x5240, "test_cvia"}, /* tbd for rdson testing , */\ + { 0x5250, "test_cvib"}, /* tbd for rdson testing , */\ + { 0x5260, "test_cvibst"}, /* tbd for rdson testing , */\ + { 0x5306, "digimuxa_sel"}, /* DigimuxA input selection control (see Digimux list for details), */\ + { 0x5376, "digimuxb_sel"}, /* DigimuxB input selection control (see Digimux list for details), */\ + { 0x5400, "hs_mode"}, /* hs_mode, high speed mode I2C bus , */\ + { 0x5412, "test_parametric_io"}, /* test_parametric_io for testing pads , */\ + { 0x5440, "enbl_ringo"}, /* enbl_ringo, for test purpose to check with ringo , */\ + { 0x5456, "digimuxc_sel"}, /* DigimuxC input selection control (see Digimux list for details), */\ + { 0x54c0, "dio_ehs"}, /* Slew control for DIO in output mode , */\ + { 0x54d0, "gainio_ehs"}, /* Slew control for GAINIO in output mode , */\ + { 0x550d, "enbl_amp"}, /* enbl_amp for testing to enable all analoge blocks in amplifier, */\ + { 0x5600, "use_direct_ctrls"}, /* use_direct_ctrls, to overrule several functions direct for testing, */\ + { 0x5610, "rst_datapath"}, /* rst_datapath, datapath reset , */\ + { 0x5620, "rst_cgu"}, /* rst_cgu, cgu reset , */\ + { 0x5637, "enbl_ref"}, /* for testing to enable all analoge blocks in references, */\ + { 0x56b0, "enbl_engage"}, /* Enable output stage amplifier , */\ + { 0x56c0, "use_direct_clk_ctrl"}, /* use_direct_clk_ctrl, to overrule several functions direct for testing, */\ + { 0x56d0, "use_direct_pll_ctrl"}, /* use_direct_pll_ctrl, to overrule several functions direct for testing, */\ + { 0x56e0, "use_direct_ctrls_2"}, /* use_direct_sourseamp_ctrls, to overrule several functions direct for testing, */\ + { 0x5707, "anamux"}, /* Anamux control , */\ + { 0x57c0, "ocptest"}, /* ctrl_ocptest, deactivates the over current protection in the power stages of the amplifier. The ocp flag signals stay active., */\ + { 0x57e0, "otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "reverse"}, /* 1: Normal mode, slope is controlled , */\ + { 0x5813, "pll_selr"}, /* pll_selr , */\ + { 0x5854, "pll_selp"}, /* pll_selp , */\ + { 0x58a5, "pll_seli"}, /* pll_seli , */\ + { 0x5950, "pll_mdec_msb"}, /* most significant bits of pll_mdec[16] , */\ + { 0x5960, "pll_ndec_msb"}, /* most significant bits of pll_ndec[9] , */\ + { 0x5970, "pll_frm"}, /* pll_frm , */\ + { 0x5980, "pll_directi"}, /* pll_directi , */\ + { 0x5990, "pll_directo"}, /* pll_directo , */\ + { 0x59a0, "enbl_pll"}, /* enbl_pll , */\ + { 0x59f0, "pll_bypass"}, /* pll_bypass , */\ + { 0x5a0f, "tsig_freq"}, /* tsig_freq, internal sinus test generator, frequency control, */\ + { 0x5b02, "tsig_freq_msb"}, /* select internal sinus test generator, frequency control msb bits, */\ + { 0x5b30, "inject_tsig"}, /* inject_tsig, control bit to switch to internal sinus test generator, */\ + { 0x5b44, "adc10_prog_sample"}, /* control ADC10 , */\ + { 0x5c0f, "pll_mdec"}, /* bits 15..0 of pll_mdec[16;0] , */\ + { 0x5d06, "pll_pdec"}, /* pll_pdec , */\ + { 0x5d78, "pll_ndec"}, /* bits 8..0 of pll_ndec[9;0] , */\ + { 0x6007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0x6185, "mtp_ecc_tcin"}, /* Mtp_ecc_tcin , */\ + { 0x6203, "mtp_man_address_in"}, /* address from I2C regs for writing one word single mtp, */\ + { 0x6260, "mtp_ecc_eeb"}, /* enable code bit generation (active low!) , */\ + { 0x6270, "mtp_ecc_ecb"}, /* enable correction signal (active low!) , */\ + { 0x6280, "man_copy_mtp_to_iic"}, /* start copying single word from mtp to i2cregs_mtp , */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* start copying single word from i2cregs_mtp to mtp [Key 1 protected], */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* start copying all the data from mtp to i2cregs_mtp, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x62d2, "mtp_speed_mode"}, /* Speed mode , */\ + { 0x6340, "mtp_direct_enable"}, /* mtp_direct_enable (key1 protected) , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr (key1 protected) , */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd (key1 protected) , */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst (key1 protected) , */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers (key1 protected) , */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg (key1 protected) , */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp (key1 protected) , */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test (key1 protected) , */\ + { 0x640f, "mtp_man_data_in"}, /* single word to be written to MTP (manual copy) , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "cf_int"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "cf_req"}, /* request for access (8 channels) , */\ + { 0x710f, "cf_madd"}, /* memory-address to be accessed , */\ + { 0x720f, "cf_mema"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "cf_err"}, /* Coolflux error flags , */\ + { 0x7387, "cf_ack"}, /* acknowledge of requests (8 channels) , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "calibr_ron_done"}, /* (key2 protected) , */\ + { 0x8105, "calibr_vout_offset"}, /* calibr_vout_offset (DCDCoffset) 2's compliment (key1 protected), */\ + { 0x8163, "calibr_delta_gain"}, /* delta gain for vamp (alpha) 2's compliment (key1 protected), */\ + { 0x81a5, "calibr_offs_amp"}, /* offset for vamp (Ampoffset) 2's compliment (key1 protected), */\ + { 0x8207, "calibr_gain_cs"}, /* gain current sense (Imeasalpha) 2's compliment (key1 protected), */\ + { 0x8284, "calibr_temp_offset"}, /* temperature offset 2's compliment (key1 protected), */\ + { 0x82d2, "calibr_temp_gain"}, /* temperature gain 2's compliment (key1 protected) , */\ + { 0x830f, "calibr_ron"}, /* Ron resistance of coil (key1 protected) , */\ + { 0x8505, "type_bits_HW"}, /* Key1_Protected_MTP5 , */\ + { 0x8601, "type_bits_1_0_SW"}, /* MTP-control SW , */\ + { 0x8681, "type_bits_8_9_SW"}, /* MTP-control SW , */\ + { 0x870f, "type_bits2_SW"}, /* MTP-control SW2 , */\ + { 0x8806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0x8870, "htol_iic_addr_en"}, /* HTOL_I2C_Address_Enable , */\ + { 0x8881, "ctrl_ovp_response"}, /* OVP response control , */\ + { 0x88a0, "disable_ovp_alarm_state"}, /* OVP alarm state control , */\ + { 0x88b0, "enbl_stretch_ovp"}, /* OVP alram strech control , */\ + { 0x88c0, "cf_debug_mode"}, /* Coolflux debug mode , */\ + { 0x8a0f, "production_data1"}, /* (key1 protected) , */\ + { 0x8b0f, "production_data2"}, /* (key1 protected) , */\ + { 0x8c0f, "production_data3"}, /* (key1 protected) , */\ + { 0x8d0f, "production_data4"}, /* (key1 protected) , */\ + { 0x8e0f, "production_data5"}, /* (key1 protected) , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa1_irq { + tfa1_irq_vdds = 0, + tfa1_irq_plls = 1, + tfa1_irq_ds = 2, + tfa1_irq_vds = 3, + tfa1_irq_uvds = 4, + tfa1_irq_cds = 5, + tfa1_irq_clks = 6, + tfa1_irq_clips = 7, + tfa1_irq_mtpb = 8, + tfa1_irq_clk = 9, + tfa1_irq_spks = 10, + tfa1_irq_acs = 11, + tfa1_irq_sws = 12, + tfa1_irq_wds = 13, + tfa1_irq_amps = 14, + tfa1_irq_arefs = 15, + tfa1_irq_ack = 32, + tfa1_irq_max = 33, + tfa1_irq_all = -1 /* all irqs */}; + +#define TFA1_IRQ_NAMETABLE static tfaIrqName_t Tfa1IrqNames[]= {\ + { 0, "VDDS"},\ + { 1, "PLLS"},\ + { 2, "DS"},\ + { 3, "VDS"},\ + { 4, "UVDS"},\ + { 5, "CDS"},\ + { 6, "CLKS"},\ + { 7, "CLIPS"},\ + { 8, "MTPB"},\ + { 9, "CLK"},\ + { 10, "SPKS"},\ + { 11, "ACS"},\ + { 12, "SWS"},\ + { 13, "WDS"},\ + { 14, "AMPS"},\ + { 15, "AREFS"},\ + { 16, "16"},\ + { 17, "17"},\ + { 18, "18"},\ + { 19, "19"},\ + { 20, "20"},\ + { 21, "21"},\ + { 22, "22"},\ + { 23, "23"},\ + { 24, "24"},\ + { 25, "25"},\ + { 26, "26"},\ + { 27, "27"},\ + { 28, "28"},\ + { 29, "29"},\ + { 30, "30"},\ + { 31, "31"},\ + { 32, "ACK"},\ + { 33, "33"},\ +}; diff --git a/sound/soc/codecs/tfa9891/tfa2_tfafieldnames_N1C.h b/sound/soc/codecs/tfa9891/tfa2_tfafieldnames_N1C.h new file mode 100755 index 000000000000..107028ec6028 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa2_tfafieldnames_N1C.h @@ -0,0 +1,1522 @@ +/** Filename: Tfa98xx_TfaFieldnames.h + * This file was generated automatically on 09/01/15 at 09:40:28. + * Source file: TFA9888_N1C_I2C_regmap_V1.xlsx + */ + +typedef enum nxpTfa2BfEnumList { + TFA2_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA2_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA2_BF_CFE = 0x0020, /*!< Enable CoolFlux */ + TFA2_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA2_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA2_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA2_BF_AMPC = 0x0060, /*!< CoolFlux controls amplifier */ + TFA2_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA2_BF_FSSSEL= 0x0091, /*!< Audio sample reference */ + TFA2_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA2_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA2_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA2_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA2_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA2_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA2_BF_MANROBOD= 0x0150, /*!< Reaction on BOD */ + TFA2_BF_BODE = 0x0160, /*!< BOD Enable */ + TFA2_BF_BODHYS= 0x0170, /*!< BOD Hysteresis */ + TFA2_BF_BODFILT= 0x0181, /*!< BOD filter */ + TFA2_BF_BODTHLVL= 0x01a1, /*!< BOD threshold */ + TFA2_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA2_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA2_BF_MANWDE= 0x01f0, /*!< Watchdog manager reaction */ + TFA2_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA2_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA2_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA2_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA2_BF_LDOBYP= 0x02c0, /*!< Receiver LDO bypass */ + TFA2_BF_REV = 0x030f, /*!< Revision info */ + TFA2_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA2_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA2_BF_SSLEFTE= 0x0500, /*!< Enable left channel */ + TFA2_BF_SSRIGHTE= 0x0510, /*!< Enable right channel */ + TFA2_BF_VSLEFTE= 0x0520, /*!< Voltage sense left */ + TFA2_BF_VSRIGHTE= 0x0530, /*!< Voltage sense right */ + TFA2_BF_CSLEFTE= 0x0540, /*!< Current sense left */ + TFA2_BF_CSRIGHTE= 0x0550, /*!< Current sense right */ + TFA2_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA2_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA2_BF_PDMSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA2_BF_SWVSTEP= 0x0e06, /*!< Register for the host SW to record the current active vstep */ + TFA2_BF_VDDS = 0x1000, /*!< POR */ + TFA2_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA2_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA2_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA2_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA2_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA2_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA2_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA2_BF_SPKS = 0x1080, /*!< Speaker error */ + TFA2_BF_ACS = 0x1090, /*!< Cold Start */ + TFA2_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA2_BF_WDS = 0x10b0, /*!< Watchdog */ + TFA2_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA2_BF_AREFS = 0x10d0, /*!< References enable */ + TFA2_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA2_BF_BODNOK= 0x10f0, /*!< BOD */ + TFA2_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA2_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA2_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA2_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA2_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA2_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA2_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA2_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA2_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA2_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA2_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA2_BF_HAPTIC= 0x11e0, /*!< Status haptic driver */ + TFA2_BF_OCPOAPL= 0x1200, /*!< OCPOK pmos A left */ + TFA2_BF_OCPOANL= 0x1210, /*!< OCPOK nmos A left */ + TFA2_BF_OCPOBPL= 0x1220, /*!< OCPOK pmos B left */ + TFA2_BF_OCPOBNL= 0x1230, /*!< OCPOK nmos B left */ + TFA2_BF_CLIPAHL= 0x1240, /*!< Clipping A left to Vddp */ + TFA2_BF_CLIPALL= 0x1250, /*!< Clipping A left to gnd */ + TFA2_BF_CLIPBHL= 0x1260, /*!< Clipping B left to Vddp */ + TFA2_BF_CLIPBLL= 0x1270, /*!< Clipping B left to gnd */ + TFA2_BF_OCPOAPRC= 0x1280, /*!< OCPOK pmos A RCV */ + TFA2_BF_OCPOANRC= 0x1290, /*!< OCPOK nmos A RCV */ + TFA2_BF_OCPOBPRC= 0x12a0, /*!< OCPOK pmos B RCV */ + TFA2_BF_OCPOBNRC= 0x12b0, /*!< OCPOK nmos B RCV */ + TFA2_BF_RCVLDOR= 0x12c0, /*!< RCV LDO regulates */ + TFA2_BF_RCVLDOBR= 0x12d0, /*!< Receiver LDO ready */ + TFA2_BF_OCDSL = 0x12e0, /*!< OCP left amplifier */ + TFA2_BF_CLIPSL= 0x12f0, /*!< Amplifier left clipping */ + TFA2_BF_OCPOAPR= 0x1300, /*!< OCPOK pmos A right */ + TFA2_BF_OCPOANR= 0x1310, /*!< OCPOK nmos A right */ + TFA2_BF_OCPOBPR= 0x1320, /*!< OCPOK pmos B right */ + TFA2_BF_OCPOBNR= 0x1330, /*!< OCPOK nmos B right */ + TFA2_BF_CLIPAHR= 0x1340, /*!< Clipping A right to Vddp */ + TFA2_BF_CLIPALR= 0x1350, /*!< Clipping A right to gnd */ + TFA2_BF_CLIPBHR= 0x1360, /*!< Clipping B left to Vddp */ + TFA2_BF_CLIPBLR= 0x1370, /*!< Clipping B right to gnd */ + TFA2_BF_OCDSR = 0x1380, /*!< OCP right amplifier */ + TFA2_BF_CLIPSR= 0x1390, /*!< Amplifier right clipping */ + TFA2_BF_OCPOKMC= 0x13a0, /*!< OCPOK MICVDD */ + TFA2_BF_MANALARM= 0x13b0, /*!< Alarm state */ + TFA2_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA2_BF_MANWAIT2= 0x13d0, /*!< Wait CF config */ + TFA2_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA2_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA2_BF_SPKSL = 0x1400, /*!< Left speaker status */ + TFA2_BF_SPKSR = 0x1410, /*!< Right speaker status */ + TFA2_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA2_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA2_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA2_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA2_BF_TDMUC = 0x2003, /*!< Usecase setting */ + TFA2_BF_TDME = 0x2040, /*!< Enable interface */ + TFA2_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA2_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA2_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA2_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA2_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA2_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA2_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA2_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA2_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA2_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA2_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA2_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA2_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA2_BF_TDMTXUS0= 0x2291, /*!< Format unused slots GAINIO */ + TFA2_BF_TDMTXUS1= 0x22b1, /*!< Format unused slots DIO1 */ + TFA2_BF_TDMTXUS2= 0x22d1, /*!< Format unused slots DIO2 */ + TFA2_BF_TDMLE = 0x2310, /*!< Control audio left */ + TFA2_BF_TDMRE = 0x2320, /*!< Control audio right */ + TFA2_BF_TDMVSRE= 0x2340, /*!< Control voltage sense right */ + TFA2_BF_TDMCSRE= 0x2350, /*!< Control current sense right */ + TFA2_BF_TDMVSLE= 0x2360, /*!< Voltage sense left control */ + TFA2_BF_TDMCSLE= 0x2370, /*!< Current sense left control */ + TFA2_BF_TDMCFRE= 0x2380, /*!< DSP out right control */ + TFA2_BF_TDMCFLE= 0x2390, /*!< DSP out left control */ + TFA2_BF_TDMCF3E= 0x23a0, /*!< AEC ref left control */ + TFA2_BF_TDMCF4E= 0x23b0, /*!< AEC ref right control */ + TFA2_BF_TDMPD1E= 0x23c0, /*!< PDM 1 control */ + TFA2_BF_TDMPD2E= 0x23d0, /*!< PDM 2 control */ + TFA2_BF_TDMLIO= 0x2421, /*!< IO audio left */ + TFA2_BF_TDMRIO= 0x2441, /*!< IO audio right */ + TFA2_BF_TDMVSRIO= 0x2481, /*!< IO voltage sense right */ + TFA2_BF_TDMCSRIO= 0x24a1, /*!< IO current sense right */ + TFA2_BF_TDMVSLIO= 0x24c1, /*!< IO voltage sense left */ + TFA2_BF_TDMCSLIO= 0x24e1, /*!< IO current sense left */ + TFA2_BF_TDMCFRIO= 0x2501, /*!< IO dspout right */ + TFA2_BF_TDMCFLIO= 0x2521, /*!< IO dspout left */ + TFA2_BF_TDMCF3IO= 0x2541, /*!< IO AEC ref left control */ + TFA2_BF_TDMCF4IO= 0x2561, /*!< IO AEC ref right control */ + TFA2_BF_TDMPD1IO= 0x2581, /*!< IO pdm1 */ + TFA2_BF_TDMPD2IO= 0x25a1, /*!< IO pdm2 */ + TFA2_BF_TDMLS = 0x2643, /*!< Position audio left */ + TFA2_BF_TDMRS = 0x2683, /*!< Position audio right */ + TFA2_BF_TDMVSRS= 0x2703, /*!< Position voltage sense right */ + TFA2_BF_TDMCSRS= 0x2743, /*!< Position current sense right */ + TFA2_BF_TDMVSLS= 0x2783, /*!< Position voltage sense left */ + TFA2_BF_TDMCSLS= 0x27c3, /*!< Position current sense left */ + TFA2_BF_TDMCFRS= 0x2803, /*!< Position dspout right */ + TFA2_BF_TDMCFLS= 0x2843, /*!< Position dspout left */ + TFA2_BF_TDMCF3S= 0x2883, /*!< Position AEC ref left control */ + TFA2_BF_TDMCF4S= 0x28c3, /*!< Position AEC ref right control */ + TFA2_BF_TDMPD1S= 0x2903, /*!< Position pdm1 */ + TFA2_BF_TDMPD2S= 0x2943, /*!< Position pdm2 */ + TFA2_BF_PDMSM = 0x3100, /*!< PDM control */ + TFA2_BF_PDMSTSEL= 0x3111, /*!< Side tone input */ + TFA2_BF_PDMLSEL= 0x3130, /*!< PDM data selection for left channel during PDM direct mode */ + TFA2_BF_PDMRSEL= 0x3140, /*!< PDM data selection for right channel during PDM direct mode */ + TFA2_BF_MICVDDE= 0x3150, /*!< Enable MICVDD */ + TFA2_BF_PDMCLRAT= 0x3201, /*!< PDM BCK/Fs ratio */ + TFA2_BF_PDMGAIN= 0x3223, /*!< PDM gain */ + TFA2_BF_PDMOSEL= 0x3263, /*!< PDM output selection - RE/FE data combination */ + TFA2_BF_SELCFHAPD= 0x32a0, /*!< Select the source for haptic data output (not for customer) */ + TFA2_BF_HAPTIME= 0x3307, /*!< Duration (ms) */ + TFA2_BF_HAPLEVEL= 0x3387, /*!< DC value (FFS) */ + TFA2_BF_GPIODIN= 0x3403, /*!< Receiving value */ + TFA2_BF_GPIOCTRL= 0x3500, /*!< GPIO master control over GPIO1/2 ports (not for customer) */ + TFA2_BF_GPIOCONF= 0x3513, /*!< Configuration */ + TFA2_BF_GPIODOUT= 0x3553, /*!< Transmitting value */ + TFA2_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA2_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA2_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA2_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA2_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA2_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA2_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA2_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA2_BF_ISTSPKS= 0x4080, /*!< Status speaker error */ + TFA2_BF_ISTACS= 0x4090, /*!< Status cold start */ + TFA2_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA2_BF_ISTWDS= 0x40b0, /*!< Status watchdog */ + TFA2_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA2_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA2_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA2_BF_ISTBODNOK= 0x40f0, /*!< Status BOD */ + TFA2_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA2_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA2_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA2_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA2_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA2_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA2_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA2_BF_ISTRCVLD= 0x4170, /*!< Status rcvldop ready */ + TFA2_BF_ISTOCPL= 0x4180, /*!< Status ocp alarm left */ + TFA2_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm right */ + TFA2_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA2_BF_ISTMWCFC= 0x41b0, /*!< Status waits CF config */ + TFA2_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA2_BF_ISTCFMER= 0x41d0, /*!< Status cfma error */ + TFA2_BF_ISTCFMAC= 0x41e0, /*!< Status cfma ack */ + TFA2_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA2_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA2_BF_ISTCLPL= 0x4210, /*!< Status clip left */ + TFA2_BF_ISTCLPR= 0x4220, /*!< Status clip right */ + TFA2_BF_ISTOCPM= 0x4230, /*!< Status mic ocpok */ + TFA2_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA2_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA2_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA2_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA2_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA2_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA2_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA2_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA2_BF_ICLSPKS= 0x4480, /*!< Clear speaker error */ + TFA2_BF_ICLACS= 0x4490, /*!< Clear cold started */ + TFA2_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA2_BF_ICLWDS= 0x44b0, /*!< Clear watchdog */ + TFA2_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA2_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA2_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA2_BF_ICLBODNOK= 0x44f0, /*!< Clear BOD */ + TFA2_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA2_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA2_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA2_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA2_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA2_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA2_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA2_BF_ICLRCVLD= 0x4570, /*!< Clear rcvldop ready */ + TFA2_BF_ICLOCPL= 0x4580, /*!< Clear ocp alarm left */ + TFA2_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm right */ + TFA2_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA2_BF_ICLMWCFC= 0x45b0, /*!< Clear wait cf config */ + TFA2_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA2_BF_ICLCFMER= 0x45d0, /*!< Clear cfma err */ + TFA2_BF_ICLCFMAC= 0x45e0, /*!< Clear cfma ack */ + TFA2_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA2_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA2_BF_ICLCLPL= 0x4610, /*!< Clear clip left */ + TFA2_BF_ICLCLPR= 0x4620, /*!< Clear clip right */ + TFA2_BF_ICLOCPM= 0x4630, /*!< Clear mic ocpok */ + TFA2_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA2_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA2_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA2_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA2_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA2_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA2_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA2_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA2_BF_IESPKS= 0x4880, /*!< Enable speaker error */ + TFA2_BF_IEACS = 0x4890, /*!< Enable cold started */ + TFA2_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA2_BF_IEWDS = 0x48b0, /*!< Enable watchdog */ + TFA2_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA2_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA2_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA2_BF_IEBODNOK= 0x48f0, /*!< Enable BOD */ + TFA2_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA2_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA2_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA2_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA2_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA2_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA2_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA2_BF_IERCVLD= 0x4970, /*!< Enable rcvldop ready */ + TFA2_BF_IEOCPL= 0x4980, /*!< Enable ocp alarm left */ + TFA2_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm right */ + TFA2_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA2_BF_IEMWCFC= 0x49b0, /*!< Enable man wait cf config */ + TFA2_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA2_BF_IECFMER= 0x49d0, /*!< Enable cfma err */ + TFA2_BF_IECFMAC= 0x49e0, /*!< Enable cfma ack */ + TFA2_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA2_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA2_BF_IECLPL= 0x4a10, /*!< Enable clip left */ + TFA2_BF_IECLPR= 0x4a20, /*!< Enable clip right */ + TFA2_BF_IEOCPM1= 0x4a30, /*!< Enable mic ocpok */ + TFA2_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA2_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA2_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA2_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA2_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA2_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA2_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA2_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA2_BF_IPOSPKS= 0x4c80, /*!< Polarity speaker error */ + TFA2_BF_IPOACS= 0x4c90, /*!< Polarity cold started */ + TFA2_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA2_BF_IPOWDS= 0x4cb0, /*!< Polarity watchdog */ + TFA2_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA2_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA2_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA2_BF_IPOBODNOK= 0x4cf0, /*!< Polarity BOD */ + TFA2_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA2_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA2_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA2_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA2_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA2_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA2_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA2_BF_IPORCVLD= 0x4d70, /*!< Polarity rcvldop ready */ + TFA2_BF_IPOOCPL= 0x4d80, /*!< Polarity ocp alarm left */ + TFA2_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm right */ + TFA2_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA2_BF_IPOMWCFC= 0x4db0, /*!< Polarity man wait cf config */ + TFA2_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA2_BF_IPOCFMER= 0x4dd0, /*!< Polarity cfma err */ + TFA2_BF_IPOCFMAC= 0x4de0, /*!< Polarity cfma ack */ + TFA2_BF_IPCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA2_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA2_BF_IPOCLPL= 0x4e10, /*!< Polarity clip left */ + TFA2_BF_IPOCLPR= 0x4e20, /*!< Polarity clip right */ + TFA2_BF_IPOOCPM= 0x4e30, /*!< Polarity mic ocpok */ + TFA2_BF_BSSCR = 0x5001, /*!< Battery protection attack Time */ + TFA2_BF_BSST = 0x5023, /*!< Battery protection threshold voltage level */ + TFA2_BF_BSSRL = 0x5061, /*!< Battery protection maximum reduction */ + TFA2_BF_BSSRR = 0x5082, /*!< Battery protection release time */ + TFA2_BF_BSSHY = 0x50b1, /*!< Battery protection hysteresis */ + TFA2_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA2_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA2_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA2_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA2_BF_CFSML = 0x5120, /*!< Soft mute FW left */ + TFA2_BF_CFSMR = 0x5130, /*!< Soft mute FW right */ + TFA2_BF_HPFBYPL= 0x5140, /*!< Bypass HPF left */ + TFA2_BF_HPFBYPR= 0x5150, /*!< Bypass HPF right */ + TFA2_BF_DPSAL = 0x5160, /*!< Enable DPSA left */ + TFA2_BF_DPSAR = 0x5170, /*!< Enable DPSA right */ + TFA2_BF_VOL = 0x5187, /*!< FW volume control for primary audio channel */ + TFA2_BF_HNDSFRCV= 0x5200, /*!< Selection receiver */ + TFA2_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA2_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA2_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA2_BF_SLOPESET= 0x52e1, /*!< Set slope */ + TFA2_BF_VOLSEC= 0x5a07, /*!< FW volume control for secondary audio channel */ + TFA2_BF_SWPROFIL= 0x5a87, /*!< Software profile data */ + TFA2_BF_DCVO = 0x7002, /*!< Boost voltage */ + TFA2_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA2_BF_DCCV = 0x7071, /*!< Coil Value */ + TFA2_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA2_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA2_BF_DCSYNCP= 0x70b2, /*!< DCDC synchronization off + 7 positions */ + TFA2_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA2_BF_RST = 0x9000, /*!< Reset */ + TFA2_BF_DMEM = 0x9011, /*!< Target memory */ + TFA2_BF_AIF = 0x9030, /*!< Auto increment */ + TFA2_BF_CFINT = 0x9040, /*!< Interrupt - auto clear */ + TFA2_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA2_BF_REQ = 0x9087, /*!< request for access (8 channels) */ + TFA2_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA2_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA2_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA2_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA2_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA2_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA2_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA2_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA2_BF_MADD = 0x910f, /*!< Memory address */ + TFA2_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA2_BF_ERR = 0x9307, /*!< Error flags */ + TFA2_BF_ACK = 0x9387, /*!< Acknowledge of requests */ + TFA2_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA2_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA2_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA2_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA2_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA2_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA2_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA2_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA2_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA2_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA2_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA2_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA2_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA2_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA2_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA2_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA2_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA2_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA2_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA2_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA2_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA2_BF_R25CL = 0xf40f, /*!< Ron resistance of left channel speaker coil */ + TFA2_BF_R25CR = 0xf50f, /*!< Ron resistance of right channel speaker coil */ +} nxpTfa2BfEnumList_t; +#define TFA2_NAMETABLE static tfaBfName_t Tfa2DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux controls amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x91, "FSSSEL"}, /* Audio sample reference , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x101, "AMPINSEL"}, /* Amplifier input selection , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x130, "MANCOLD"}, /* Execute cold start , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x150, "MANROBOD"}, /* Reaction on BOD , */\ + { 0x160, "BODE"}, /* BOD Enable , */\ + { 0x170, "BODHYS"}, /* BOD Hysteresis , */\ + { 0x181, "BODFILT"}, /* BOD filter , */\ + { 0x1a1, "BODTHLVL"}, /* BOD threshold , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "RCVNS"}, /* Noise shaper selection , */\ + { 0x1f0, "MANWDE"}, /* Watchdog manager reaction , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "LDOBYP"}, /* Receiver LDO bypass , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x500, "SSLEFTE"}, /* Enable left channel , */\ + { 0x510, "SSRIGHTE"}, /* Enable right channel , */\ + { 0x520, "VSLEFTE"}, /* Voltage sense left , */\ + { 0x530, "VSRIGHTE"}, /* Voltage sense right , */\ + { 0x540, "CSLEFTE"}, /* Current sense left , */\ + { 0x550, "CSRIGHTE"}, /* Current sense right , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "PDMSMUTE"}, /* Side tone soft mute , */\ + { 0xe06, "SWVSTEP"}, /* Register for the host SW to record the current active vstep, */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "CLKS"}, /* Clocks stable , */\ + { 0x1060, "MTPB"}, /* MTP busy , */\ + { 0x1070, "NOCLK"}, /* Lost clock , */\ + { 0x1080, "SPKS"}, /* Speaker error , */\ + { 0x1090, "ACS"}, /* Cold Start , */\ + { 0x10a0, "SWS"}, /* Amplifier engage , */\ + { 0x10b0, "WDS"}, /* Watchdog , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x10f0, "BODNOK"}, /* BOD , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "STMUTEB"}, /* side tone (un)mute busy , */\ + { 0x1180, "STMUTE"}, /* side tone mute state , */\ + { 0x1190, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x11a2, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11d0, "TDMERR"}, /* TDM error , */\ + { 0x11e0, "HAPTIC"}, /* Status haptic driver , */\ + { 0x1200, "OCPOAPL"}, /* OCPOK pmos A left , */\ + { 0x1210, "OCPOANL"}, /* OCPOK nmos A left , */\ + { 0x1220, "OCPOBPL"}, /* OCPOK pmos B left , */\ + { 0x1230, "OCPOBNL"}, /* OCPOK nmos B left , */\ + { 0x1240, "CLIPAHL"}, /* Clipping A left to Vddp , */\ + { 0x1250, "CLIPALL"}, /* Clipping A left to gnd , */\ + { 0x1260, "CLIPBHL"}, /* Clipping B left to Vddp , */\ + { 0x1270, "CLIPBLL"}, /* Clipping B left to gnd , */\ + { 0x1280, "OCPOAPRC"}, /* OCPOK pmos A RCV , */\ + { 0x1290, "OCPOANRC"}, /* OCPOK nmos A RCV , */\ + { 0x12a0, "OCPOBPRC"}, /* OCPOK pmos B RCV , */\ + { 0x12b0, "OCPOBNRC"}, /* OCPOK nmos B RCV , */\ + { 0x12c0, "RCVLDOR"}, /* RCV LDO regulates , */\ + { 0x12d0, "RCVLDOBR"}, /* Receiver LDO ready , */\ + { 0x12e0, "OCDSL"}, /* OCP left amplifier , */\ + { 0x12f0, "CLIPSL"}, /* Amplifier left clipping , */\ + { 0x1300, "OCPOAPR"}, /* OCPOK pmos A right , */\ + { 0x1310, "OCPOANR"}, /* OCPOK nmos A right , */\ + { 0x1320, "OCPOBPR"}, /* OCPOK pmos B right , */\ + { 0x1330, "OCPOBNR"}, /* OCPOK nmos B right , */\ + { 0x1340, "CLIPAHR"}, /* Clipping A right to Vddp , */\ + { 0x1350, "CLIPALR"}, /* Clipping A right to gnd , */\ + { 0x1360, "CLIPBHR"}, /* Clipping B left to Vddp , */\ + { 0x1370, "CLIPBLR"}, /* Clipping B right to gnd , */\ + { 0x1380, "OCDSR"}, /* OCP right amplifier , */\ + { 0x1390, "CLIPSR"}, /* Amplifier right clipping , */\ + { 0x13a0, "OCPOKMC"}, /* OCPOK MICVDD , */\ + { 0x13b0, "MANALARM"}, /* Alarm state , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13d0, "MANWAIT2"}, /* Wait CF config , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1400, "SPKSL"}, /* Left speaker status , */\ + { 0x1410, "SPKSR"}, /* Right speaker status , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x2003, "TDMUC"}, /* Usecase setting , */\ + { 0x2040, "TDME"}, /* Enable interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x20b0, "TDMFSPOL"}, /* FS polarity , */\ + { 0x20c3, "TDMNBCK"}, /* N-BCK's in FS , */\ + { 0x2103, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x2144, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2194, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x21e0, "TDMDEL"}, /* data delay to FS , */\ + { 0x21f0, "TDMADJ"}, /* data adjustment , */\ + { 0x2201, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2224, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2271, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "TDMTXUS1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "TDMTXUS2"}, /* Format unused slots DIO2 , */\ + { 0x2310, "TDMLE"}, /* Control audio left , */\ + { 0x2320, "TDMRE"}, /* Control audio right , */\ + { 0x2340, "TDMVSRE"}, /* Control voltage sense right , */\ + { 0x2350, "TDMCSRE"}, /* Control current sense right , */\ + { 0x2360, "TDMVSLE"}, /* Voltage sense left control , */\ + { 0x2370, "TDMCSLE"}, /* Current sense left control , */\ + { 0x2380, "TDMCFRE"}, /* DSP out right control , */\ + { 0x2390, "TDMCFLE"}, /* DSP out left control , */\ + { 0x23a0, "TDMCF3E"}, /* AEC ref left control , */\ + { 0x23b0, "TDMCF4E"}, /* AEC ref right control , */\ + { 0x23c0, "TDMPD1E"}, /* PDM 1 control , */\ + { 0x23d0, "TDMPD2E"}, /* PDM 2 control , */\ + { 0x2421, "TDMLIO"}, /* IO audio left , */\ + { 0x2441, "TDMRIO"}, /* IO audio right , */\ + { 0x2481, "TDMVSRIO"}, /* IO voltage sense right , */\ + { 0x24a1, "TDMCSRIO"}, /* IO current sense right , */\ + { 0x24c1, "TDMVSLIO"}, /* IO voltage sense left , */\ + { 0x24e1, "TDMCSLIO"}, /* IO current sense left , */\ + { 0x2501, "TDMCFRIO"}, /* IO dspout right , */\ + { 0x2521, "TDMCFLIO"}, /* IO dspout left , */\ + { 0x2541, "TDMCF3IO"}, /* IO AEC ref left control , */\ + { 0x2561, "TDMCF4IO"}, /* IO AEC ref right control , */\ + { 0x2581, "TDMPD1IO"}, /* IO pdm1 , */\ + { 0x25a1, "TDMPD2IO"}, /* IO pdm2 , */\ + { 0x2643, "TDMLS"}, /* Position audio left , */\ + { 0x2683, "TDMRS"}, /* Position audio right , */\ + { 0x2703, "TDMVSRS"}, /* Position voltage sense right , */\ + { 0x2743, "TDMCSRS"}, /* Position current sense right , */\ + { 0x2783, "TDMVSLS"}, /* Position voltage sense left , */\ + { 0x27c3, "TDMCSLS"}, /* Position current sense left , */\ + { 0x2803, "TDMCFRS"}, /* Position dspout right , */\ + { 0x2843, "TDMCFLS"}, /* Position dspout left , */\ + { 0x2883, "TDMCF3S"}, /* Position AEC ref left control , */\ + { 0x28c3, "TDMCF4S"}, /* Position AEC ref right control , */\ + { 0x2903, "TDMPD1S"}, /* Position pdm1 , */\ + { 0x2943, "TDMPD2S"}, /* Position pdm2 , */\ + { 0x3100, "PDMSM"}, /* PDM control , */\ + { 0x3111, "PDMSTSEL"}, /* Side tone input , */\ + { 0x3130, "PDMLSEL"}, /* PDM data selection for left channel during PDM direct mode, */\ + { 0x3140, "PDMRSEL"}, /* PDM data selection for right channel during PDM direct mode, */\ + { 0x3150, "MICVDDE"}, /* Enable MICVDD , */\ + { 0x3201, "PDMCLRAT"}, /* PDM BCK/Fs ratio , */\ + { 0x3223, "PDMGAIN"}, /* PDM gain , */\ + { 0x3263, "PDMOSEL"}, /* PDM output selection - RE/FE data combination , */\ + { 0x32a0, "SELCFHAPD"}, /* Select the source for haptic data output (not for customer), */\ + { 0x3307, "HAPTIME"}, /* Duration (ms) , */\ + { 0x3387, "HAPLEVEL"}, /* DC value (FFS) , */\ + { 0x3403, "GPIODIN"}, /* Receiving value , */\ + { 0x3500, "GPIOCTRL"}, /* GPIO master control over GPIO1/2 ports (not for customer), */\ + { 0x3513, "GPIOCONF"}, /* Configuration , */\ + { 0x3553, "GPIODOUT"}, /* Transmitting value , */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTPLLS"}, /* Status PLL lock , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOVDS"}, /* Status OVP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTCLKS"}, /* Status clocks stable , */\ + { 0x4060, "ISTMTPB"}, /* Status MTP busy , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4080, "ISTSPKS"}, /* Status speaker error , */\ + { 0x4090, "ISTACS"}, /* Status cold start , */\ + { 0x40a0, "ISTSWS"}, /* Status amplifier engage , */\ + { 0x40b0, "ISTWDS"}, /* Status watchdog , */\ + { 0x40c0, "ISTAMPS"}, /* Status amplifier enable , */\ + { 0x40d0, "ISTAREFS"}, /* Status Ref enable , */\ + { 0x40e0, "ISTADCCR"}, /* Status Control ADC , */\ + { 0x40f0, "ISTBODNOK"}, /* Status BOD , */\ + { 0x4100, "ISTBSTCU"}, /* Status DCDC current limiting , */\ + { 0x4110, "ISTBSTHI"}, /* Status DCDC active , */\ + { 0x4120, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4130, "ISTBSTPKCUR"}, /* Status bst peakcur , */\ + { 0x4140, "ISTBSTVC"}, /* Status DCDC level 1x , */\ + { 0x4150, "ISTBST86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "ISTBST93"}, /* Status DCDC level 1.07x , */\ + { 0x4170, "ISTRCVLD"}, /* Status rcvldop ready , */\ + { 0x4180, "ISTOCPL"}, /* Status ocp alarm left , */\ + { 0x4190, "ISTOCPR"}, /* Status ocp alarm right , */\ + { 0x41a0, "ISTMWSRC"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "ISTMWCFC"}, /* Status waits CF config , */\ + { 0x41c0, "ISTMWSMU"}, /* Status Audio mute sequence , */\ + { 0x41d0, "ISTCFMER"}, /* Status cfma error , */\ + { 0x41e0, "ISTCFMAC"}, /* Status cfma ack , */\ + { 0x41f0, "ISTCLKOOR"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4210, "ISTCLPL"}, /* Status clip left , */\ + { 0x4220, "ISTCLPR"}, /* Status clip right , */\ + { 0x4230, "ISTOCPM"}, /* Status mic ocpok , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLPLLS"}, /* Clear PLL lock , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOVDS"}, /* Clear OVP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLCLKS"}, /* Clear clocks stable , */\ + { 0x4460, "ICLMTPB"}, /* Clear mtp busy , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4480, "ICLSPKS"}, /* Clear speaker error , */\ + { 0x4490, "ICLACS"}, /* Clear cold started , */\ + { 0x44a0, "ICLSWS"}, /* Clear amplifier engage , */\ + { 0x44b0, "ICLWDS"}, /* Clear watchdog , */\ + { 0x44c0, "ICLAMPS"}, /* Clear enbl amp , */\ + { 0x44d0, "ICLAREFS"}, /* Clear ref enable , */\ + { 0x44e0, "ICLADCCR"}, /* Clear control ADC , */\ + { 0x44f0, "ICLBODNOK"}, /* Clear BOD , */\ + { 0x4500, "ICLBSTCU"}, /* Clear DCDC current limiting , */\ + { 0x4510, "ICLBSTHI"}, /* Clear DCDC active , */\ + { 0x4520, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4530, "ICLBSTPC"}, /* Clear bst peakcur , */\ + { 0x4540, "ICLBSTVC"}, /* Clear DCDC level 1x , */\ + { 0x4550, "ICLBST86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "ICLBST93"}, /* Clear DCDC level 1.07x , */\ + { 0x4570, "ICLRCVLD"}, /* Clear rcvldop ready , */\ + { 0x4580, "ICLOCPL"}, /* Clear ocp alarm left , */\ + { 0x4590, "ICLOCPR"}, /* Clear ocp alarm right , */\ + { 0x45a0, "ICLMWSRC"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "ICLMWCFC"}, /* Clear wait cf config , */\ + { 0x45c0, "ICLMWSMU"}, /* Clear audio mute sequence , */\ + { 0x45d0, "ICLCFMER"}, /* Clear cfma err , */\ + { 0x45e0, "ICLCFMAC"}, /* Clear cfma ack , */\ + { 0x45f0, "ICLCLKOOR"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4610, "ICLCLPL"}, /* Clear clip left , */\ + { 0x4620, "ICLCLPR"}, /* Clear clip right , */\ + { 0x4630, "ICLOCPM"}, /* Clear mic ocpok , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEPLLS"}, /* Enable pll lock , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOVDS"}, /* Enable OVP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IECLKS"}, /* Enable clocks stable , */\ + { 0x4860, "IEMTPB"}, /* Enable mtp busy , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4880, "IESPKS"}, /* Enable speaker error , */\ + { 0x4890, "IEACS"}, /* Enable cold started , */\ + { 0x48a0, "IESWS"}, /* Enable amplifier engage , */\ + { 0x48b0, "IEWDS"}, /* Enable watchdog , */\ + { 0x48c0, "IEAMPS"}, /* Enable enbl amp , */\ + { 0x48d0, "IEAREFS"}, /* Enable ref enable , */\ + { 0x48e0, "IEADCCR"}, /* Enable Control ADC , */\ + { 0x48f0, "IEBODNOK"}, /* Enable BOD , */\ + { 0x4900, "IEBSTCU"}, /* Enable DCDC current limiting , */\ + { 0x4910, "IEBSTHI"}, /* Enable DCDC active , */\ + { 0x4920, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4930, "IEBSTPC"}, /* Enable bst peakcur , */\ + { 0x4940, "IEBSTVC"}, /* Enable DCDC level 1x , */\ + { 0x4950, "IEBST86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "IEBST93"}, /* Enable DCDC level 1.07x , */\ + { 0x4970, "IERCVLD"}, /* Enable rcvldop ready , */\ + { 0x4980, "IEOCPL"}, /* Enable ocp alarm left , */\ + { 0x4990, "IEOCPR"}, /* Enable ocp alarm right , */\ + { 0x49a0, "IEMWSRC"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "IEMWCFC"}, /* Enable man wait cf config , */\ + { 0x49c0, "IEMWSMU"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "IECFMER"}, /* Enable cfma err , */\ + { 0x49e0, "IECFMAC"}, /* Enable cfma ack , */\ + { 0x49f0, "IECLKOOR"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "IETDMER"}, /* Enable tdm error , */\ + { 0x4a10, "IECLPL"}, /* Enable clip left , */\ + { 0x4a20, "IECLPR"}, /* Enable clip right , */\ + { 0x4a30, "IEOCPM1"}, /* Enable mic ocpok , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOPLLS"}, /* Polarity pll lock , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOVDS"}, /* Polarity OVP alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOCLKS"}, /* Polarity clocks stable , */\ + { 0x4c60, "IPOMTPB"}, /* Polarity mtp busy , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4c80, "IPOSPKS"}, /* Polarity speaker error , */\ + { 0x4c90, "IPOACS"}, /* Polarity cold started , */\ + { 0x4ca0, "IPOSWS"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "IPOWDS"}, /* Polarity watchdog , */\ + { 0x4cc0, "IPOAMPS"}, /* Polarity enbl amp , */\ + { 0x4cd0, "IPOAREFS"}, /* Polarity ref enable , */\ + { 0x4ce0, "IPOADCCR"}, /* Polarity Control ADC , */\ + { 0x4cf0, "IPOBODNOK"}, /* Polarity BOD , */\ + { 0x4d00, "IPOBSTCU"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "IPOBSTHI"}, /* Polarity DCDC active , */\ + { 0x4d20, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "IPOBSTPC"}, /* Polarity bst peakcur , */\ + { 0x4d40, "IPOBSTVC"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "IPOBST86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "IPOBST93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d70, "IPORCVLD"}, /* Polarity rcvldop ready , */\ + { 0x4d80, "IPOOCPL"}, /* Polarity ocp alarm left , */\ + { 0x4d90, "IPOOCPR"}, /* Polarity ocp alarm right , */\ + { 0x4da0, "IPOMWSRC"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "IPOMWCFC"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "IPOMWSMU"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "IPOCFMER"}, /* Polarity cfma err , */\ + { 0x4de0, "IPOCFMAC"}, /* Polarity cfma ack , */\ + { 0x4df0, "IPCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e10, "IPOCLPL"}, /* Polarity clip left , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip right , */\ + { 0x4e30, "IPOOCPM"}, /* Polarity mic ocpok , */\ + { 0x5001, "BSSCR"}, /* Battery protection attack Time , */\ + { 0x5023, "BSST"}, /* Battery protection threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery protection maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery protection release time , */\ + { 0x50b1, "BSSHY"}, /* Battery protection hysteresis , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass HW clipper , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5110, "INTSMUTE"}, /* Soft mute HW , */\ + { 0x5120, "CFSML"}, /* Soft mute FW left , */\ + { 0x5130, "CFSMR"}, /* Soft mute FW right , */\ + { 0x5140, "HPFBYPL"}, /* Bypass HPF left , */\ + { 0x5150, "HPFBYPR"}, /* Bypass HPF right , */\ + { 0x5160, "DPSAL"}, /* Enable DPSA left , */\ + { 0x5170, "DPSAR"}, /* Enable DPSA right , */\ + { 0x5187, "VOL"}, /* FW volume control for primary audio channel , */\ + { 0x5200, "HNDSFRCV"}, /* Selection receiver , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e1, "SLOPESET"}, /* Set slope , */\ + { 0x5a07, "VOLSEC"}, /* FW volume control for secondary audio channel , */\ + { 0x5a87, "SWPROFIL"}, /* Software profile data , */\ + { 0x7002, "DCVO"}, /* Boost voltage , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Coil Value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70b2, "DCSYNCP"}, /* DCDC synchronization off + 7 positions , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x9000, "RST"}, /* Reset , */\ + { 0x9011, "DMEM"}, /* Target memory , */\ + { 0x9030, "AIF"}, /* Auto increment , */\ + { 0x9040, "CFINT"}, /* Interrupt - auto clear , */\ + { 0x9050, "CFCGATE"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x9090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x90a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x90b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x90d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x910f, "MADD"}, /* Memory address , */\ + { 0x920f, "MEMA"}, /* Activate memory access , */\ + { 0x9307, "ERR"}, /* Error flags , */\ + { 0x9387, "ACK"}, /* Acknowledge of requests , */\ + { 0x9380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "USERDEF"}, /* Calibration delta current limit DCDC , */\ + { 0xf40f, "R25CL"}, /* Ron resistance of left channel speaker coil , */\ + { 0xf50f, "R25CR"}, /* Ron resistance of right channel speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA2_BITNAMETABLE static tfaBfName_t Tfa2BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x50, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x60, "sel_enbl_amplifier"}, /* CoolFlux controls amplifier , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0x91, "fs_pulse_sel"}, /* Audio sample reference , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x101, "vamp_sel"}, /* Amplifier input selection , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x130, "execute_cold_start"}, /* Execute cold start , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x150, "man_enbl_brown_out"}, /* Reaction on BOD , */\ + { 0x160, "enbl_bod"}, /* BOD Enable , */\ + { 0x170, "enbl_bod_hyst"}, /* BOD Hysteresis , */\ + { 0x181, "bod_delay"}, /* BOD filter , */\ + { 0x1a1, "bod_lvlsel"}, /* BOD threshold , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1e0, "pwm_sel_rcv_ns"}, /* Noise shaper selection , */\ + { 0x1f0, "man_enbl_watchdog"}, /* Watchdog manager reaction , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2b0, "bypass_hvbat_filter"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "ctrl_rcvldop_bypass"}, /* Receiver LDO bypass , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x500, "enbl_spkr_ss_left"}, /* Enable left channel , */\ + { 0x510, "enbl_spkr_ss_right"}, /* Enable right channel , */\ + { 0x520, "enbl_volsense_left"}, /* Voltage sense left , */\ + { 0x530, "enbl_volsense_right"}, /* Voltage sense right , */\ + { 0x540, "enbl_cursense_left"}, /* Current sense left , */\ + { 0x550, "enbl_cursense_right"}, /* Current sense right , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0xd00, "side_tone_gain_sel"}, /* PDM side tone gain selector , */\ + { 0xd18, "side_tone_gain"}, /* Side tone gain , */\ + { 0xda0, "mute_side_tone"}, /* Side tone soft mute , */\ + { 0xe06, "ctrl_digtoana"}, /* Register for the host SW to record the current active vstep, */\ + { 0xe70, "enbl_cmfb_left"}, /* Current sense common mode feedback control for left channel, */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm , */\ + { 0x1030, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm , */\ + { 0x1050, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1060, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock , */\ + { 0x1080, "flag_cf_speakererror"}, /* Speaker error , */\ + { 0x1090, "flag_cold_started"}, /* Cold Start , */\ + { 0x10a0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10b0, "flag_watchdog_reset"}, /* Watchdog , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x10f0, "flag_bod_vddd_nok"}, /* BOD , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos , */\ + { 0x1130, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1150, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1160, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1170, "flag_soft_mute_busy"}, /* side tone (un)mute busy , */\ + { 0x1180, "flag_soft_mute_state"}, /* side tone mute state , */\ + { 0x1190, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x11a2, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11d0, "flag_tdm_error"}, /* TDM error , */\ + { 0x11e0, "flag_haptic_busy"}, /* Status haptic driver , */\ + { 0x1200, "flag_ocpokap_left"}, /* OCPOK pmos A left , */\ + { 0x1210, "flag_ocpokan_left"}, /* OCPOK nmos A left , */\ + { 0x1220, "flag_ocpokbp_left"}, /* OCPOK pmos B left , */\ + { 0x1230, "flag_ocpokbn_left"}, /* OCPOK nmos B left , */\ + { 0x1240, "flag_clipa_high_left"}, /* Clipping A left to Vddp , */\ + { 0x1250, "flag_clipa_low_left"}, /* Clipping A left to gnd , */\ + { 0x1260, "flag_clipb_high_left"}, /* Clipping B left to Vddp , */\ + { 0x1270, "flag_clipb_low_left"}, /* Clipping B left to gnd , */\ + { 0x1280, "flag_ocpokap_rcv"}, /* OCPOK pmos A RCV , */\ + { 0x1290, "flag_ocpokan_rcv"}, /* OCPOK nmos A RCV , */\ + { 0x12a0, "flag_ocpokbp_rcv"}, /* OCPOK pmos B RCV , */\ + { 0x12b0, "flag_ocpokbn_rcv"}, /* OCPOK nmos B RCV , */\ + { 0x12c0, "flag_rcvldop_ready"}, /* RCV LDO regulates , */\ + { 0x12d0, "flag_rcvldop_bypassready"}, /* Receiver LDO ready , */\ + { 0x12e0, "flag_ocp_alarm_left"}, /* OCP left amplifier , */\ + { 0x12f0, "flag_clip_left"}, /* Amplifier left clipping , */\ + { 0x1300, "flag_ocpokap_right"}, /* OCPOK pmos A right , */\ + { 0x1310, "flag_ocpokan_right"}, /* OCPOK nmos A right , */\ + { 0x1320, "flag_ocpokbp_right"}, /* OCPOK pmos B right , */\ + { 0x1330, "flag_ocpokbn_right"}, /* OCPOK nmos B right , */\ + { 0x1340, "flag_clipa_high_right"}, /* Clipping A right to Vddp , */\ + { 0x1350, "flag_clipa_low_right"}, /* Clipping A right to gnd , */\ + { 0x1360, "flag_clipb_high_right"}, /* Clipping B left to Vddp , */\ + { 0x1370, "flag_clipb_low_right"}, /* Clipping B right to gnd , */\ + { 0x1380, "flag_ocp_alarm_right"}, /* OCP right amplifier , */\ + { 0x1390, "flag_clip_right"}, /* Amplifier right clipping , */\ + { 0x13a0, "flag_mic_ocpok"}, /* OCPOK MICVDD , */\ + { 0x13b0, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13d0, "flag_man_wait_cf_config"}, /* Wait CF config , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1400, "flag_cf_speakererror_left"}, /* Left speaker status , */\ + { 0x1410, "flag_cf_speakererror_right"}, /* Right speaker status , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x2003, "tdm_usecase"}, /* Usecase setting , */\ + { 0x2040, "tdm_enable"}, /* Enable interface , */\ + { 0x2050, "tdm_mode"}, /* Slave/master , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x20b0, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x20c3, "tdm_nbck"}, /* N-BCK's in FS , */\ + { 0x2103, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x2144, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2194, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x21e0, "tdm_data_delay"}, /* data delay to FS , */\ + { 0x21f0, "tdm_data_adjustment"}, /* data adjustment , */\ + { 0x2201, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2224, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2271, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x2291, "tdm_txdata_format_unused_slot_sd0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "tdm_txdata_format_unused_slot_sd1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "tdm_txdata_format_unused_slot_sd2"}, /* Format unused slots DIO2 , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control gainin (not used in DSP) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio left , */\ + { 0x2320, "tdm_sink2_enable"}, /* Control audio right , */\ + { 0x2330, "tdm_source0_enable"}, /* Control gainout (not used in DSP) , */\ + { 0x2340, "tdm_source1_enable"}, /* Control voltage sense right , */\ + { 0x2350, "tdm_source2_enable"}, /* Control current sense right , */\ + { 0x2360, "tdm_source3_enable"}, /* Voltage sense left control , */\ + { 0x2370, "tdm_source4_enable"}, /* Current sense left control , */\ + { 0x2380, "tdm_source5_enable"}, /* DSP out right control , */\ + { 0x2390, "tdm_source6_enable"}, /* DSP out left control , */\ + { 0x23a0, "tdm_source7_enable"}, /* AEC ref left control , */\ + { 0x23b0, "tdm_source8_enable"}, /* AEC ref right control , */\ + { 0x23c0, "tdm_source9_enable"}, /* PDM 1 control , */\ + { 0x23d0, "tdm_source10_enable"}, /* PDM 2 control , */\ + { 0x2401, "tdm_sink0_io"}, /* IO gainin (not used in DSP) , */\ + { 0x2421, "tdm_sink1_io"}, /* IO audio left , */\ + { 0x2441, "tdm_sink2_io"}, /* IO audio right , */\ + { 0x2461, "tdm_source0_io"}, /* IO gainout (not used in DSP) , */\ + { 0x2481, "tdm_source1_io"}, /* IO voltage sense right , */\ + { 0x24a1, "tdm_source2_io"}, /* IO current sense right , */\ + { 0x24c1, "tdm_source3_io"}, /* IO voltage sense left , */\ + { 0x24e1, "tdm_source4_io"}, /* IO current sense left , */\ + { 0x2501, "tdm_source5_io"}, /* IO dspout right , */\ + { 0x2521, "tdm_source6_io"}, /* IO dspout left , */\ + { 0x2541, "tdm_source7_io"}, /* IO AEC ref left control , */\ + { 0x2561, "tdm_source8_io"}, /* IO AEC ref right control , */\ + { 0x2581, "tdm_source9_io"}, /* IO pdm1 , */\ + { 0x25a1, "tdm_source10_io"}, /* IO pdm2 , */\ + { 0x2603, "tdm_sink0_slot"}, /* Position gainin (not used in DSP) , */\ + { 0x2643, "tdm_sink1_slot"}, /* Position audio left , */\ + { 0x2683, "tdm_sink2_slot"}, /* Position audio right , */\ + { 0x26c3, "tdm_source0_slot"}, /* Position gainout (not used in DSP) , */\ + { 0x2703, "tdm_source1_slot"}, /* Position voltage sense right , */\ + { 0x2743, "tdm_source2_slot"}, /* Position current sense right , */\ + { 0x2783, "tdm_source3_slot"}, /* Position voltage sense left , */\ + { 0x27c3, "tdm_source4_slot"}, /* Position current sense left , */\ + { 0x2803, "tdm_source5_slot"}, /* Position dspout right , */\ + { 0x2843, "tdm_source6_slot"}, /* Position dspout left , */\ + { 0x2883, "tdm_source7_slot"}, /* Position AEC ref left control , */\ + { 0x28c3, "tdm_source8_slot"}, /* Position AEC ref right control , */\ + { 0x2903, "tdm_source9_slot"}, /* Position pdm1 , */\ + { 0x2943, "tdm_source10_slot"}, /* Position pdm2 , */\ + { 0x3100, "pdm_mode"}, /* PDM control , */\ + { 0x3111, "pdm_side_tone_sel"}, /* Side tone input , */\ + { 0x3130, "pdm_left_sel"}, /* PDM data selection for left channel during PDM direct mode, */\ + { 0x3140, "pdm_right_sel"}, /* PDM data selection for right channel during PDM direct mode, */\ + { 0x3150, "enbl_micvdd"}, /* Enable MICVDD , */\ + { 0x3160, "bypass_micvdd_ocp"}, /* Bypass control for the MICVDD OCP flag processing , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 0x3223, "pdm_gain"}, /* PDM gain , */\ + { 0x3263, "sel_pdm_out_data"}, /* PDM output selection - RE/FE data combination , */\ + { 0x32a0, "sel_cf_haptic_data"}, /* Select the source for haptic data output (not for customer), */\ + { 0x3307, "haptic_duration"}, /* Duration (ms) , */\ + { 0x3387, "haptic_data"}, /* DC value (FFS) , */\ + { 0x3403, "gpio_datain"}, /* Receiving value , */\ + { 0x3500, "gpio_ctrl"}, /* GPIO master control over GPIO1/2 ports (not for customer), */\ + { 0x3513, "gpio_dir"}, /* Configuration , */\ + { 0x3553, "gpio_dataout"}, /* Transmitting value , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_pll_lock"}, /* Status PLL lock , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ovpok"}, /* Status OVP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_clocks_stable"}, /* Status clocks stable , */\ + { 0x4060, "int_out_flag_mtp_busy"}, /* Status MTP busy , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4080, "int_out_flag_cf_speakererror"}, /* Status speaker error , */\ + { 0x4090, "int_out_flag_cold_started"}, /* Status cold start , */\ + { 0x40a0, "int_out_flag_engage"}, /* Status amplifier engage , */\ + { 0x40b0, "int_out_flag_watchdog_reset"}, /* Status watchdog , */\ + { 0x40c0, "int_out_flag_enbl_amp"}, /* Status amplifier enable , */\ + { 0x40d0, "int_out_flag_enbl_ref"}, /* Status Ref enable , */\ + { 0x40e0, "int_out_flag_adc10_ready"}, /* Status Control ADC , */\ + { 0x40f0, "int_out_flag_bod_vddd_nok"}, /* Status BOD , */\ + { 0x4100, "int_out_flag_bst_bstcur"}, /* Status DCDC current limiting , */\ + { 0x4110, "int_out_flag_bst_hiz"}, /* Status DCDC active , */\ + { 0x4120, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4130, "int_out_flag_bst_peakcur"}, /* Status bst peakcur , */\ + { 0x4140, "int_out_flag_bst_voutcomp"}, /* Status DCDC level 1x , */\ + { 0x4150, "int_out_flag_bst_voutcomp86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "int_out_flag_bst_voutcomp93"}, /* Status DCDC level 1.07x , */\ + { 0x4170, "int_out_flag_rcvldop_ready"}, /* Status rcvldop ready , */\ + { 0x4180, "int_out_flag_ocp_alarm_left"}, /* Status ocp alarm left , */\ + { 0x4190, "int_out_flag_ocp_alarm_right"}, /* Status ocp alarm right , */\ + { 0x41a0, "int_out_flag_man_wait_src_settings"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "int_out_flag_man_wait_cf_config"}, /* Status waits CF config , */\ + { 0x41c0, "int_out_flag_man_start_mute_audio"}, /* Status Audio mute sequence , */\ + { 0x41d0, "int_out_flag_cfma_err"}, /* Status cfma error , */\ + { 0x41e0, "int_out_flag_cfma_ack"}, /* Status cfma ack , */\ + { 0x41f0, "int_out_flag_clk_out_of_range"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4210, "int_out_flag_clip_left"}, /* Status clip left , */\ + { 0x4220, "int_out_flag_clip_right"}, /* Status clip right , */\ + { 0x4230, "int_out_flag_mic_ocpok"}, /* Status mic ocpok , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_pll_lock"}, /* Clear PLL lock , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ovpok"}, /* Clear OVP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_clocks_stable"}, /* Clear clocks stable , */\ + { 0x4460, "int_in_flag_mtp_busy"}, /* Clear mtp busy , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4480, "int_in_flag_cf_speakererror"}, /* Clear speaker error , */\ + { 0x4490, "int_in_flag_cold_started"}, /* Clear cold started , */\ + { 0x44a0, "int_in_flag_engage"}, /* Clear amplifier engage , */\ + { 0x44b0, "int_in_flag_watchdog_reset"}, /* Clear watchdog , */\ + { 0x44c0, "int_in_flag_enbl_amp"}, /* Clear enbl amp , */\ + { 0x44d0, "int_in_flag_enbl_ref"}, /* Clear ref enable , */\ + { 0x44e0, "int_in_flag_adc10_ready"}, /* Clear control ADC , */\ + { 0x44f0, "int_in_flag_bod_vddd_nok"}, /* Clear BOD , */\ + { 0x4500, "int_in_flag_bst_bstcur"}, /* Clear DCDC current limiting , */\ + { 0x4510, "int_in_flag_bst_hiz"}, /* Clear DCDC active , */\ + { 0x4520, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4530, "int_in_flag_bst_peakcur"}, /* Clear bst peakcur , */\ + { 0x4540, "int_in_flag_bst_voutcomp"}, /* Clear DCDC level 1x , */\ + { 0x4550, "int_in_flag_bst_voutcomp86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "int_in_flag_bst_voutcomp93"}, /* Clear DCDC level 1.07x , */\ + { 0x4570, "int_in_flag_rcvldop_ready"}, /* Clear rcvldop ready , */\ + { 0x4580, "int_in_flag_ocp_alarm_left"}, /* Clear ocp alarm left , */\ + { 0x4590, "int_in_flag_ocp_alarm_right"}, /* Clear ocp alarm right , */\ + { 0x45a0, "int_in_flag_man_wait_src_settings"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "int_in_flag_man_wait_cf_config"}, /* Clear wait cf config , */\ + { 0x45c0, "int_in_flag_man_start_mute_audio"}, /* Clear audio mute sequence , */\ + { 0x45d0, "int_in_flag_cfma_err"}, /* Clear cfma err , */\ + { 0x45e0, "int_in_flag_cfma_ack"}, /* Clear cfma ack , */\ + { 0x45f0, "int_in_flag_clk_out_of_range"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4610, "int_in_flag_clip_left"}, /* Clear clip left , */\ + { 0x4620, "int_in_flag_clip_right"}, /* Clear clip right , */\ + { 0x4630, "int_in_flag_mic_ocpok"}, /* Clear mic ocpok , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_pll_lock"}, /* Enable pll lock , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ovpok"}, /* Enable OVP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_clocks_stable"}, /* Enable clocks stable , */\ + { 0x4860, "int_enable_flag_mtp_busy"}, /* Enable mtp busy , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4880, "int_enable_flag_cf_speakererror"}, /* Enable speaker error , */\ + { 0x4890, "int_enable_flag_cold_started"}, /* Enable cold started , */\ + { 0x48a0, "int_enable_flag_engage"}, /* Enable amplifier engage , */\ + { 0x48b0, "int_enable_flag_watchdog_reset"}, /* Enable watchdog , */\ + { 0x48c0, "int_enable_flag_enbl_amp"}, /* Enable enbl amp , */\ + { 0x48d0, "int_enable_flag_enbl_ref"}, /* Enable ref enable , */\ + { 0x48e0, "int_enable_flag_adc10_ready"}, /* Enable Control ADC , */\ + { 0x48f0, "int_enable_flag_bod_vddd_nok"}, /* Enable BOD , */\ + { 0x4900, "int_enable_flag_bst_bstcur"}, /* Enable DCDC current limiting , */\ + { 0x4910, "int_enable_flag_bst_hiz"}, /* Enable DCDC active , */\ + { 0x4920, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4930, "int_enable_flag_bst_peakcur"}, /* Enable bst peakcur , */\ + { 0x4940, "int_enable_flag_bst_voutcomp"}, /* Enable DCDC level 1x , */\ + { 0x4950, "int_enable_flag_bst_voutcomp86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "int_enable_flag_bst_voutcomp93"}, /* Enable DCDC level 1.07x , */\ + { 0x4970, "int_enable_flag_rcvldop_ready"}, /* Enable rcvldop ready , */\ + { 0x4980, "int_enable_flag_ocp_alarm_left"}, /* Enable ocp alarm left , */\ + { 0x4990, "int_enable_flag_ocp_alarm_right"}, /* Enable ocp alarm right , */\ + { 0x49a0, "int_enable_flag_man_wait_src_settings"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "int_enable_flag_man_wait_cf_config"}, /* Enable man wait cf config , */\ + { 0x49c0, "int_enable_flag_man_start_mute_audio"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "int_enable_flag_cfma_err"}, /* Enable cfma err , */\ + { 0x49e0, "int_enable_flag_cfma_ack"}, /* Enable cfma ack , */\ + { 0x49f0, "int_enable_flag_clk_out_of_range"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4a10, "int_enable_flag_clip_left"}, /* Enable clip left , */\ + { 0x4a20, "int_enable_flag_clip_right"}, /* Enable clip right , */\ + { 0x4a30, "int_enable_flag_mic_ocpok"}, /* Enable mic ocpok , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_pll_lock"}, /* Polarity pll lock , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ovpok"}, /* Polarity OVP alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_clocks_stable"}, /* Polarity clocks stable , */\ + { 0x4c60, "int_polarity_flag_mtp_busy"}, /* Polarity mtp busy , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4c80, "int_polarity_flag_cf_speakererror"}, /* Polarity speaker error , */\ + { 0x4c90, "int_polarity_flag_cold_started"}, /* Polarity cold started , */\ + { 0x4ca0, "int_polarity_flag_engage"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "int_polarity_flag_watchdog_reset"}, /* Polarity watchdog , */\ + { 0x4cc0, "int_polarity_flag_enbl_amp"}, /* Polarity enbl amp , */\ + { 0x4cd0, "int_polarity_flag_enbl_ref"}, /* Polarity ref enable , */\ + { 0x4ce0, "int_polarity_flag_adc10_ready"}, /* Polarity Control ADC , */\ + { 0x4cf0, "int_polarity_flag_bod_vddd_nok"}, /* Polarity BOD , */\ + { 0x4d00, "int_polarity_flag_bst_bstcur"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "int_polarity_flag_bst_hiz"}, /* Polarity DCDC active , */\ + { 0x4d20, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "int_polarity_flag_bst_peakcur"}, /* Polarity bst peakcur , */\ + { 0x4d40, "int_polarity_flag_bst_voutcomp"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "int_polarity_flag_bst_voutcomp86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "int_polarity_flag_bst_voutcomp93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d70, "int_polarity_flag_rcvldop_ready"}, /* Polarity rcvldop ready , */\ + { 0x4d80, "int_polarity_flag_ocp_alarm_left"}, /* Polarity ocp alarm left , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm_right"}, /* Polarity ocp alarm right , */\ + { 0x4da0, "int_polarity_flag_man_wait_src_settings"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "int_polarity_flag_man_wait_cf_config"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "int_polarity_flag_man_start_mute_audio"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "int_polarity_flag_cfma_err"}, /* Polarity cfma err , */\ + { 0x4de0, "int_polarity_flag_cfma_ack"}, /* Polarity cfma ack , */\ + { 0x4df0, "int_polarity_flag_clk_out_of_range"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4e10, "int_polarity_flag_clip_left"}, /* Polarity clip left , */\ + { 0x4e20, "int_polarity_flag_clip_right"}, /* Polarity clip right , */\ + { 0x4e30, "int_polarity_flag_mic_ocpok"}, /* Polarity mic ocpok , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery protection attack Time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery protection threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery protection maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery protection release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery protection hysteresis , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass HW clipper , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5110, "soft_mute"}, /* Soft mute HW , */\ + { 0x5120, "cf_mute_left"}, /* Soft mute FW left , */\ + { 0x5130, "cf_mute_right"}, /* Soft mute FW right , */\ + { 0x5140, "bypass_hp_left"}, /* Bypass HPF left , */\ + { 0x5150, "bypass_hp_right"}, /* Bypass HPF right , */\ + { 0x5160, "enbl_dpsa_left"}, /* Enable DPSA left , */\ + { 0x5170, "enbl_dpsa_right"}, /* Enable DPSA right , */\ + { 0x5187, "cf_volume"}, /* FW volume control for primary audio channel , */\ + { 0x5200, "ctrl_rcv"}, /* Selection receiver , */\ + { 0x5210, "ctrl_rcv_fb_100k"}, /* Selection of feedback resistor for receiver mode (not for customer), */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e1, "ctrl_slope"}, /* Set slope , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for battery protection, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5360, "enbl_low_latency"}, /* CF low latency outputs for add module , */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5420, "fb_hz"}, /* Feedback resistor set to high ohmic , */\ + { 0x5430, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5440, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5450, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x5503, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5543, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5581, "dpsa_drive"}, /* Control of the number of power stage sections, total of 4 sections. Each section is 1/4 of the total power stages., */\ + { 0x560a, "enbl_amp_left"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually - Left channel, */\ + { 0x56b0, "enbl_engage_left"}, /* Enables/engage power stage and control loop - left channel, */\ + { 0x570a, "enbl_amp_right"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually - Right channel, */\ + { 0x57b0, "enbl_engage_right"}, /* Enables/engage power stage and control loop - right channel, */\ + { 0x5800, "hard_mute_left"}, /* Hard mute - PWM module left , */\ + { 0x5810, "hard_mute_right"}, /* Hard mute - PWM module right , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5830, "pwm_bitlength"}, /* PWM bit length in noise shaper , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58b0, "enbl_pwm_phase_shift_left"}, /* Control for pwm phase shift, inverted function - left channel, */\ + { 0x58c0, "enbl_pwm_phase_shift_right"}, /* Control for pwm phase shift - right channel , */\ + { 0x5900, "ctrl_rcvldop_pulldown"}, /* Pulldown of LDO (2.7V) , */\ + { 0x5910, "ctrl_rcvldop_test_comp"}, /* Enable testing of LDO comparator , */\ + { 0x5920, "ctrl_rcvldop_test_loadedldo"}, /* Load connected to rcvldo , */\ + { 0x5930, "enbl_rcvldop"}, /* Enables the LDO (2.7) , */\ + { 0x5a07, "cf_volume_sec"}, /* FW volume control for secondary audio channel , */\ + { 0x5a87, "sw_profile"}, /* Software profile data , */\ + { 0x7002, "boost_volt"}, /* Boost voltage , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_coil_value"}, /* Coil Value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70b2, "dcdc_synchronisation"}, /* DCDC synchronization off + 7 positions , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x7104, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7151, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7174, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x71c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x71e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x71f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7200, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac , */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7332, "bst_freq"}, /* DCDC bost frequency control , */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8040, "cs_dc_offset"}, /* Current sense decimator offset control , */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8110, "invertpwm_left"}, /* Current sense common mode feedback pwm invert control for left channel, */\ + { 0x8122, "cmfb_gain_left"}, /* Current sense common mode feedback control gain for left channel, */\ + { 0x8154, "cmfb_offset_left"}, /* Current sense common mode feedback control offset for left channel, */\ + { 0x8200, "enbl_cmfb_right"}, /* Current sense common mode feedback control for right channel, */\ + { 0x8210, "invertpwm_right"}, /* Current sense common mode feedback pwm invert control for right channel, */\ + { 0x8222, "cmfb_gain_right"}, /* Current sense common mode feedback control gain for right channel, */\ + { 0x8254, "cmfb_offset_right"}, /* Current sense common mode feedback control offset for right channel, */\ + { 0x8305, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP)*signal, */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8440, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8453, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x8490, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x84a4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8500, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8510, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8530, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8540, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8550, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8560, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8574, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8600, "enbl_cs_adc_left"}, /* Enable current sense ADC , */\ + { 0x8610, "enbl_cs_inn1_left"}, /* Enable connection of current sense negative1 , */\ + { 0x8630, "enbl_cs_inp1_left"}, /* Enable connection of current sense positive1 , */\ + { 0x8650, "enbl_cs_ldo_left"}, /* Enable current sense LDO , */\ + { 0x8660, "enbl_cs_nofloating_n_left"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8670, "enbl_cs_nofloating_p_left"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8680, "enbl_cs_vbatldo_left"}, /* Enable of current sense LDO , */\ + { 0x8700, "enbl_cs_adc_right"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1_right"}, /* Enable connection of current sense negative1 , */\ + { 0x8730, "enbl_cs_inp1_right"}, /* Enable connection of current sense positive1 , */\ + { 0x8750, "enbl_cs_ldo_right"}, /* Enable current sense LDO , */\ + { 0x8760, "enbl_cs_nofloating_n_right"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p_right"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo_right"}, /* Enable of current sense LDO , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "volsense_dc_offset"}, /* Voltage sense decimator offset control , */\ + { 0x9000, "cf_rst_dsp"}, /* Reset , */\ + { 0x9011, "cf_dmem"}, /* Target memory , */\ + { 0x9030, "cf_aif"}, /* Auto increment , */\ + { 0x9040, "cf_int"}, /* Interrupt - auto clear , */\ + { 0x9050, "cf_cgate_off"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "cf_req_cmd"}, /* Firmware event request rpc command , */\ + { 0x9090, "cf_req_reset"}, /* Firmware event request reset restart , */\ + { 0x90a0, "cf_req_mips"}, /* Firmware event request short on mips , */\ + { 0x90b0, "cf_req_mute_ready"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "cf_req_volume_ready"}, /* Firmware event request volume ready , */\ + { 0x90d0, "cf_req_damage"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "cf_req_calibrate_ready"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "cf_req_reserved"}, /* Firmware event request reserved , */\ + { 0x910f, "cf_madd"}, /* Memory address , */\ + { 0x920f, "cf_mema"}, /* Activate memory access , */\ + { 0x9307, "cf_err"}, /* Error flags , */\ + { 0x9387, "cf_ack"}, /* Acknowledge of requests , */\ + { 0x9380, "cf_ack_cmd"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "cf_ack_reset"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "cf_ack_mips"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "cf_ack_mute_ready"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "cf_ack_volume_ready"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "cf_ack_damage"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "cf_ack_calibrate_ready"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "cf_ack_reserved"}, /* Firmware event acknowledge reserved , */\ + { 0x980f, "ivt_addr0_msb"}, /* Coolflux interrupt vector table address0 MSB , */\ + { 0x990f, "ivt_addr0_lsb"}, /* Coolflux interrupt vector table address0 LSB , */\ + { 0x9a0f, "ivt_addr1_msb"}, /* Coolflux interrupt vector table address1 MSB , */\ + { 0x9b0f, "ivt_addr1_lsb"}, /* Coolflux interrupt vector table address1 LSB , */\ + { 0x9c0f, "ivt_addr2_msb"}, /* Coolflux interrupt vector table address2 MSB , */\ + { 0x9d0f, "ivt_addr2_lsb"}, /* Coolflux interrupt vector table address2 LSB , */\ + { 0x9e0f, "ivt_addr3_msb"}, /* Coolflux interrupt vector table address3 MSB , */\ + { 0x9f0f, "ivt_addr3_lsb"}, /* Coolflux interrupt vector table address3 LSB , */\ + { 0xa007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xa107, "mtpkey2"}, /* MTP KEY2 register , */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa400, "faim_set_clkws"}, /* Sets the faim controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the faim are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the faim are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb010, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb020, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb030, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb040, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb050, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb060, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb070, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3c0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0xc3d0, "test_abistfft_enbl"}, /* FFT Coolflux , */\ + { 0xc3e0, "test_pwr_switch"}, /* Test mode for digital power switches core sw/mem sw/micvdd sw, */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc540, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc550, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc600, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc613, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc650, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc660, "bypass_diosw_ovp"}, /* Bypass ovp for memory switch diosw , */\ + { 0xc670, "enbl_powerswitch"}, /* Vddd core power switch control - overrules the manager control, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to GPIO1 (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to GPIO2 (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to GPIO3 (see Digimux list for details), */\ + { 0xc887, "digimuxd_sel"}, /* DigimuxD input selection control routed to GPIO4 (see Digimux list for details), */\ + { 0xc901, "dio1_ehs"}, /* Speed/load setting for DIO1 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc921, "dio2_ehs"}, /* Speed/load setting for DIO2 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc941, "gainio_ehs"}, /* Speed/load setting for GAINIO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc961, "pdmo_ehs"}, /* Speed/load setting for PDMO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9a1, "tdo_ehs"}, /* Speed/load setting for TDO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xca00, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xca10, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xca20, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xca30, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xca40, "enbl_anamux5"}, /* Enable anamux5 , */\ + { 0xca50, "enbl_anamux6"}, /* Enable anamux6 , */\ + { 0xca60, "enbl_anamux7"}, /* Enable anamux7 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb54, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba4, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xcc04, "anamux5"}, /* Anamux selection control - anamux on TEST5 , */\ + { 0xcc54, "anamux6"}, /* Anamux selection control - anamux on TEST6 , */\ + { 0xcca4, "anamux7"}, /* Anamux selection control - anamux on TEST7 , */\ + { 0xcd05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xcd64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcdb3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcdf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xce09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcea0, "pll_mdec_msb"}, /* MSB of pll_mdec - I2C direct PLL control mode only, */\ + { 0xceb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcec0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xced0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcee0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcef0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcf0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xd006, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xd10f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xd202, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xd230, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd243, "tsig_gain_left"}, /* Test signal gain for left channel , */\ + { 0xd283, "tsig_gain_right"}, /* Test signal gain for right channel , */\ + { 0xd300, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd311, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd332, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd364, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd3b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd3c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd409, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd506, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd570, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf163, "calibr_gain_left"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "calibr_offset_left"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain_right"}, /* HW gain module - right channel (2's complement) , */\ + { 0xf245, "calibr_offset_right"}, /* Offset for amplifier, HW gain module - right channel (2's complement), */\ + { 0xf2a3, "calibr_rcvldop_trim"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "calibr_gain_cs_left"}, /* Current sense gain - left channel (signed two's complement format), */\ + { 0xf387, "calibr_gain_cs_right"}, /* Current sense gain - right channel (signed two's complement format), */\ + { 0xf40f, "calibr_R25C_L"}, /* Ron resistance of left channel speaker coil , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of right channel speaker coil , */\ + { 0xf606, "ctrl_offset_a_left"}, /* Offset of left amplifier level shifter A , */\ + { 0xf686, "ctrl_offset_b_left"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a_right"}, /* Offset of right amplifier level shifter A , */\ + { 0xf786, "ctrl_offset_b_right"}, /* Offset of right amplifier level shifter B , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf900, "mtp_lock_dcdcoff_mode"}, /* Disable function dcdcoff_mode , */\ + { 0xf910, "mtp_lock_enbl_coolflux"}, /* Disable function enbl_coolflux , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Disable programming of max dcdc boost voltage , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf987, "type_bits_fw"}, /* MTP-control FW - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA (key1 protected) , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB (key1 protected) , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC (key1 protected) , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD (key1 protected) , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE (key1 protected) , */\ + { 0xff05, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa2_irq { + tfa2_irq_stvdds = 0, + tfa2_irq_stplls = 1, + tfa2_irq_stotds = 2, + tfa2_irq_stovds = 3, + tfa2_irq_stuvds = 4, + tfa2_irq_stclks = 5, + tfa2_irq_stmtpb = 6, + tfa2_irq_stnoclk = 7, + tfa2_irq_stspks = 8, + tfa2_irq_stacs = 9, + tfa2_irq_stsws = 10, + tfa2_irq_stwds = 11, + tfa2_irq_stamps = 12, + tfa2_irq_starefs = 13, + tfa2_irq_stadccr = 14, + tfa2_irq_stbodnok = 15, + tfa2_irq_stbstcu = 16, + tfa2_irq_stbsthi = 17, + tfa2_irq_stbstoc = 18, + tfa2_irq_stbstpkcur = 19, + tfa2_irq_stbstvc = 20, + tfa2_irq_stbst86 = 21, + tfa2_irq_stbst93 = 22, + tfa2_irq_strcvld = 23, + tfa2_irq_stocpl = 24, + tfa2_irq_stocpr = 25, + tfa2_irq_stmwsrc = 26, + tfa2_irq_stmwcfc = 27, + tfa2_irq_stmwsmu = 28, + tfa2_irq_stcfmer = 29, + tfa2_irq_stcfmac = 30, + tfa2_irq_stclkoor = 31, + tfa2_irq_sttdmer = 32, + tfa2_irq_stclpl = 33, + tfa2_irq_stclpr = 34, + tfa2_irq_stocpm = 35, + tfa2_irq_max = 36, + tfa2_irq_all = -1 /* all irqs */}; + +#define TFA2_IRQ_NAMETABLE static tfaIrqName_t Tfa2IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STPLLS"},\ + { 2, "STOTDS"},\ + { 3, "STOVDS"},\ + { 4, "STUVDS"},\ + { 5, "STCLKS"},\ + { 6, "STMTPB"},\ + { 7, "STNOCLK"},\ + { 8, "STSPKS"},\ + { 9, "STACS"},\ + { 10, "STSWS"},\ + { 11, "STWDS"},\ + { 12, "STAMPS"},\ + { 13, "STAREFS"},\ + { 14, "STADCCR"},\ + { 15, "STBODNOK"},\ + { 16, "STBSTCU"},\ + { 17, "STBSTHI"},\ + { 18, "STBSTOC"},\ + { 19, "STBSTPKCUR"},\ + { 20, "STBSTVC"},\ + { 21, "STBST86"},\ + { 22, "STBST93"},\ + { 23, "STRCVLD"},\ + { 24, "STOCPL"},\ + { 25, "STOCPR"},\ + { 26, "STMWSRC"},\ + { 27, "STMWCFC"},\ + { 28, "STMWSMU"},\ + { 29, "STCFMER"},\ + { 30, "STCFMAC"},\ + { 31, "STCLKOOR"},\ + { 32, "STTDMER"},\ + { 33, "STCLPL"},\ + { 34, "STCLPR"},\ + { 35, "STOCPM"},\ + { 36, "36"},\ +}; diff --git a/sound/soc/codecs/tfa9891/tfa9887B_init.c b/sound/soc/codecs/tfa9891/tfa9887B_init.c new file mode 100755 index 000000000000..52b03bc6e45f --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9887B_init.c @@ -0,0 +1,76 @@ +/* + *Copyright 2014,2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + +#ifdef TFA98XX_FULL +/** clockless way to determine if this is the tfa9887 + * by testing if the PVP bit is writable + */ +int tfa9887B_is87(Tfa98xx_handle_t handle) +{ + unsigned short save_value, check_value; + + tfa98xx_read_register16(handle, 0x08, &save_value); + if ( (save_value&0x0400) == 0 ) // if clear it's 87 + return 1; + /* try to clear pvp bit */ + tfa98xx_write_register16(handle, 0x08, (save_value & ~0x0400)); + tfa98xx_read_register16(handle, 0x08, &check_value); + /* restore */ + tfa98xx_write_register16(handle, 0x08, save_value); + /* could we write the bit */ + return (check_value!=save_value) ? 1 : 0; // if changed it's the 87 +} +#endif + +static enum Tfa98xx_Error tfa9887B_specific(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int result; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default */ + + result = TFA_SET_BF(handle, AMPE, 1); + if (result < 0) + return -result; + + /* some other registers must be set for optimal amplifier behaviour */ + tfa98xx_write_register16(handle, 0x05, 0x13AB); + tfa98xx_write_register16(handle, 0x06, 0x001F); + /* peak voltage protection is always on, but may be written */ + tfa98xx_write_register16(handle, 0x08, 0x3C4E); + /*TFA98XX_SYSCTRL_DCA=0*/ + tfa98xx_write_register16(handle, 0x09, 0x024D); + tfa98xx_write_register16(handle, 0x41, 0x0308); + error = tfa98xx_write_register16(handle, 0x49, 0x0E82); + + return error; +} + +/* + * register device specifics functions + */ +void tfa9887B_ops(struct tfa_device_ops *ops) { + ops->tfa_init=tfa9887B_specific; +} diff --git a/sound/soc/codecs/tfa9891/tfa9887_init.c b/sound/soc/codecs/tfa9891/tfa9887_init.c new file mode 100755 index 000000000000..e899ca841c62 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9887_init.c @@ -0,0 +1,55 @@ +/* + *Copyright 2014,215 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + +static enum Tfa98xx_Error tfa9887_specific(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int result; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default */ + + result = TFA_SET_BF(handle, AMPE, 1); + if (result < 0) + return -result; + + /* some other registers must be set for optimal amplifier behaviour */ + tfa98xx_write_register16(handle, 0x05, 0x13AB); + tfa98xx_write_register16(handle, 0x06, 0x001F); + tfa98xx_write_register16(handle, 0x08, 0x3C4E); + /*TFA98XX_SYSCTRL_DCA=0*/ + tfa98xx_write_register16(handle, 0x09, 0x024D); + tfa98xx_write_register16(handle, 0x0A, 0x3EC3); + tfa98xx_write_register16(handle, 0x41, 0x0308); + error = tfa98xx_write_register16(handle, 0x49, 0x0E82); + + return error; +} + +/* + * register device specifics functions + */ +void tfa9887_ops(struct tfa_device_ops *ops) { + ops->tfa_init=tfa9887_specific; +} diff --git a/sound/soc/codecs/tfa9891/tfa9887_tfafieldnames.h b/sound/soc/codecs/tfa9891/tfa9887_tfafieldnames.h new file mode 100755 index 000000000000..912e331881c6 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9887_tfafieldnames.h @@ -0,0 +1,60 @@ +/** Filename: Tfa9887_TfaFieldnames.h + * This file was generated automatically on 04/14/15 at 10:23:40. + * Source file: TFA9897N1B_I2C_list_URT_source_v34_87Only.xls + */ + +#define TFA9887_NAMETABLE static tfaBfName_t Tfa9887DatasheetNames[]= {\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input: , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x500, "BSSBY"}, /* , */\ + { 0x511, "BSSCR"}, /* 00 = 0.56 dB/Sample , */\ + { 0x532, "BSST"}, /* 000 = 2.92V , */\ + { 0x5f0, "I2SDOC"}, /* selection data out , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0x4134, "PWMDEL"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWMSH"}, /* PWM Shape , */\ + { 0x4190, "PWMRE"}, /* PWM Bitlength in noise shaper , */\ + { 0x48e1, "TCC"}, /* sample & hold track time: , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9887_BITNAMETABLE static tfaBfName_t Tfa9887BitNames[]= {\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input: , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x500, "bypass_clipper"}, /* , */\ + { 0x511, "vbat_prot_attacktime[1:0]"}, /* 00 = 0.56 dB/Sample , */\ + { 0x532, "vbat_prot_thlevel[2:0]"}, /* 000 = 2.92V , */\ + { 0x5d0, "reset_min_vbat"}, /* to reset the clipper via I2C in case the CF is bypassed, */\ + { 0x5f0, "datao_sel"}, /* selection data out , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0x4100, "bypass_hp"}, /* bypass_hp, to bypass the hp filter byhind the CoolFlux, */\ + { 0x4110, "hard_mute"}, /* hard mute setting in HW , */\ + { 0x4120, "soft_mute"}, /* Soft mute setting in HW , */\ + { 0x4134, "PWM_Delay[4:0]"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWM_Shape"}, /* PWM Shape , */\ + { 0x4190, "PWM_BitLength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4800, "ctrl_negin"}, /* , */\ + { 0x4810, "ctrl_cs_sein"}, /* , */\ + { 0x4820, "ctrl_coincidencecs"}, /* HIGH => Prevent dcdc switching during clk_cs_clksh, */\ + { 0x4876, "delay_se_neg[6:0]"}, /* delayshiftse2 , */\ + { 0x48e1, "ctrl_cs_ttrack[1:0]"}, /* sample & hold track time: , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + diff --git a/sound/soc/codecs/tfa9891/tfa9888_init.c b/sound/soc/codecs/tfa9891/tfa9888_init.c new file mode 100755 index 000000000000..68f9e07f9a63 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9888_init.c @@ -0,0 +1,177 @@ +/* + * initTfa9888.c + * + * Created on: Jun 26, 2014 + * Author: wim + */ + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + + +static enum Tfa98xx_Error tfa9888_specific(Tfa98xx_handle_t handle) { + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + if ((handles_local[handle].rev & 0xff) != 0x88) { + pr_err("This code is not for this device type: %x\n", handles_local[handle].rev); + return Tfa98xx_Error_Bad_Parameter; + } + + /* Unlock keys to write settings */ + error = tfa98xx_write_register16(handle, 0x0F, 0x5A6B); + error = tfa98xx_read_register16(handle, 0xFB, &value); + xor = value ^ 0x005A; + error = tfa98xx_write_register16(handle, 0xA0, xor); + + /* The optimal settings are different for 1c, 2c, 3b and 2b/1b */ + if (handles_local[handle].rev == 0x2c88) { + /* ----- generated code start ----- */ + /* --------- Version v1 ---------- */ + tfa98xx_write_register16(handle, 0x00, 0x164d); //POR=0x064d + tfa98xx_write_register16(handle, 0x01, 0x828b); //POR=0x92cb + tfa98xx_write_register16(handle, 0x02, 0x1dc8); //POR=0x1828 + tfa98xx_write_register16(handle, 0x0e, 0x0080); //POR=0x0000 + tfa98xx_write_register16(handle, 0x20, 0x089e); //POR=0x0890 + tfa98xx_write_register16(handle, 0x22, 0x543c); //POR=0x545c + tfa98xx_write_register16(handle, 0x23, 0x0006); //POR=0x0000 + tfa98xx_write_register16(handle, 0x24, 0x0014); //POR=0x0000 + tfa98xx_write_register16(handle, 0x25, 0x000a); //POR=0x0000 + tfa98xx_write_register16(handle, 0x26, 0x0100); //POR=0x0000 + tfa98xx_write_register16(handle, 0x28, 0x1000); //POR=0x0000 + tfa98xx_write_register16(handle, 0x51, 0x0000); //POR=0x00c0 + tfa98xx_write_register16(handle, 0x52, 0xfafe); //POR=0xbaf6 + tfa98xx_write_register16(handle, 0x70, 0x3ee4); //POR=0x3ee6 + tfa98xx_write_register16(handle, 0x71, 0x1074); //POR=0x3074 + tfa98xx_write_register16(handle, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } else if (handles_local[handle].rev == 0x1c88) { + /* ----- generated code start ----- */ + /* --------- Version v6 ---------- */ + tfa98xx_write_register16(handle, 0x00, 0x164d); //POR=0x064d + tfa98xx_write_register16(handle, 0x01, 0x828b); //POR=0x92cb + tfa98xx_write_register16(handle, 0x02, 0x1dc8); //POR=0x1828 + tfa98xx_write_register16(handle, 0x0e, 0x0080); //POR=0x0000 + tfa98xx_write_register16(handle, 0x20, 0x089e); //POR=0x0890 + tfa98xx_write_register16(handle, 0x22, 0x543c); //POR=0x545c + tfa98xx_write_register16(handle, 0x23, 0x0006); //POR=0x0000 + tfa98xx_write_register16(handle, 0x24, 0x0014); //POR=0x0000 + tfa98xx_write_register16(handle, 0x25, 0x000a); //POR=0x0000 + tfa98xx_write_register16(handle, 0x26, 0x0100); //POR=0x0000 + tfa98xx_write_register16(handle, 0x28, 0x1000); //POR=0x0000 + tfa98xx_write_register16(handle, 0x51, 0x0000); //POR=0x00c0 + tfa98xx_write_register16(handle, 0x52, 0xfafe); //POR=0xbaf6 + tfa98xx_write_register16(handle, 0x70, 0x3ee4); //POR=0x3ee6 + tfa98xx_write_register16(handle, 0x71, 0x1074); //POR=0x3074 + tfa98xx_write_register16(handle, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } else if (handles_local[handle].rev == 0x3b88) { + /* ----- generated code start ----- */ + /* --------- Version v20 ---------- */ + tfa98xx_write_register16(handle, 0x01, 0x828b); //POR=0x92cb + tfa98xx_write_register16(handle, 0x02, 0x1dc8); //POR=0x1828 + tfa98xx_write_register16(handle, 0x20, 0x089e); //POR=0x0890 + tfa98xx_write_register16(handle, 0x22, 0x543c); //POR=0x545c + tfa98xx_write_register16(handle, 0x23, 0x0c06); //POR=0x0000 + tfa98xx_write_register16(handle, 0x24, 0x0014); //POR=0x0000 + tfa98xx_write_register16(handle, 0x25, 0x000a); //POR=0x0000 + tfa98xx_write_register16(handle, 0x26, 0x0100); //POR=0x0000 + tfa98xx_write_register16(handle, 0x28, 0x1000); //POR=0x0000 + tfa98xx_write_register16(handle, 0x51, 0x0000); //POR=0x00c0 + tfa98xx_write_register16(handle, 0x52, 0xfafe); //POR=0xbaf6 + tfa98xx_write_register16(handle, 0x58, 0x1e1c); //POR=0x161c + tfa98xx_write_register16(handle, 0x70, 0x3ee4); //POR=0x3ee6 + tfa98xx_write_register16(handle, 0x71, 0x1074); //POR=0x3074 + tfa98xx_write_register16(handle, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } else { + /* If not 1c or 3b assume older version */ + /* ----- generated code start ----- */ + /* --------- Version v19 ---------- */ + tfa98xx_write_register16(handle, 0x00, 0x1e5d); //POR=0x064d + tfa98xx_write_register16(handle, 0x01, 0x828b); //POR=0x92cb + tfa98xx_write_register16(handle, 0x20, 0x089e); //POR=0x0890 + tfa98xx_write_register16(handle, 0x23, 0x0c06); //POR=0x0000 + tfa98xx_write_register16(handle, 0x24, 0x0014); //POR=0x0000 + tfa98xx_write_register16(handle, 0x25, 0x000a); //POR=0x0000 + tfa98xx_write_register16(handle, 0x26, 0x0100); //POR=0x0000 + tfa98xx_write_register16(handle, 0x28, 0x1000); //POR=0x0000 + tfa98xx_write_register16(handle, 0x51, 0x0000); //POR=0x00c0 + tfa98xx_write_register16(handle, 0x52, 0x9ae2); //POR=0xbaf6 + tfa98xx_write_register16(handle, 0x58, 0x1e1c); //POR=0x161c + tfa98xx_write_register16(handle, 0x70, 0x3ce6); //POR=0x3ee6 + tfa98xx_write_register16(handle, 0x71, 0x1074); //POR=0x3074 + tfa98xx_write_register16(handle, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } + + return error; +} + +static enum Tfa98xx_Error tfa9888_tfa_dsp_write_tables(Tfa98xx_handle_t handle, int sample_rate) +{ + unsigned char buffer[15] = {0}; + int size = 15 * sizeof(char); + + /* Write the fractional delay in the hardware register 'cs_frac_delay' */ + switch(sample_rate) { + case 0: /* 8kHz */ + TFA_SET_BF(handle, FRACTDEL, 40); + break; + case 1: /* 11.025KHz */ + TFA_SET_BF(handle, FRACTDEL, 38); + break; + case 2: /* 12kHz */ + TFA_SET_BF(handle, FRACTDEL, 37); + break; + case 3: /* 16kHz */ + TFA_SET_BF(handle, FRACTDEL, 59); + break; + case 4: /* 22.05KHz */ + TFA_SET_BF(handle, FRACTDEL, 56); + break; + case 5: /* 24kHz */ + TFA_SET_BF(handle, FRACTDEL, 56); + break; + case 6: /* 32kHz */ + TFA_SET_BF(handle, FRACTDEL, 52); + break; + case 7: /* 44.1kHz */ + TFA_SET_BF(handle, FRACTDEL, 48); + break; + case 8: + default:/* 48kHz */ + TFA_SET_BF(handle, FRACTDEL, 46); + break; + } + + /* First copy the msg_id to the buffer */ + buffer[0] = (uint8_t) 0; + buffer[1] = (uint8_t) MODULE_FRAMEWORK + 128; + buffer[2] = (uint8_t) FW_PAR_ID_SET_SENSES_DELAY; + + /* Required for all FS exept 8kHz (8kHz is all zero) */ + if(sample_rate != 0) { + buffer[5] = 1; /* Vdelay_P */ + buffer[8] = 0; /* Idelay_P */ + buffer[11] = 1; /* Vdelay_S */ + buffer[14] = 0; /* Idelay_S */ + } + + /* send SetSensesDelay msg */ + return tfa_dsp_msg(handle, size, (char *)buffer); +} + +/* + * register device specifics functions + */ +void tfa9888_ops(struct tfa_device_ops *ops) { + ops->tfa_init = tfa9888_specific; + ops->tfa_dsp_write_tables=tfa9888_tfa_dsp_write_tables; +} diff --git a/sound/soc/codecs/tfa9891/tfa9890_init.c b/sound/soc/codecs/tfa9891/tfa9890_init.c new file mode 100755 index 000000000000..595779ae2a69 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9890_init.c @@ -0,0 +1,178 @@ +/* + *Copyright 2014,2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + +static enum Tfa98xx_Error tfa9890_specific(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short regRead = 0; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default for N1C2 */ + + /* some PLL registers must be set optimal for amplifier behaviour + */ + error = tfa98xx_write_register16(handle, 0x40, 0x5a6b); + if (error) + return error; + tfa98xx_read_register16(handle, 0x59, ®Read); + regRead |= 0x3; + tfa98xx_write_register16(handle, 0x59, regRead); + error = tfa98xx_write_register16(handle, 0x40, 0x0000); + + error = tfa98xx_write_register16(handle, 0x47, 0x7BE1); + + return error; +} + +/* + * Tfa9890_DspSystemStable will compensate for the wrong behavior of CLKS + * to determine if the DSP subsystem is ready for patch and config loading. + * + * A MTP calibration register is checked for non-zero. + * + * Note: This only works after i2c reset as this will clear the MTP contents. + * When we are configured then the DSP communication will synchronize access. + * + */ +static enum Tfa98xx_Error tfa9890_dsp_system_stable(Tfa98xx_handle_t handle, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short status, mtp0; + int result, tries; + + /* check the contents of the STATUS register */ + result = TFA_READ_REG(handle, AREFS); + if (result < 0) { + error = -result; + goto errorExit; + } + status = (unsigned short)result; + + /* if AMPS is set then we were already configured and running + * no need to check further + */ + *ready = (TFA_GET_BF_VALUE(handle, AMPS, status) == 1); + if (*ready) /* if ready go back */ + return error; /* will be Tfa98xx_Error_Ok */ + + /* check AREFS and CLKS: not ready if either is clear */ + *ready = !((TFA_GET_BF_VALUE(handle, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(handle, CLKS, status) == 0)); + if (!*ready) /* if not ready go back */ + return error; /* will be Tfa98xx_Error_Ok */ + + /* check MTPB + * mtpbusy will be active when the subsys copies MTP to I2C + * 2 times retry avoids catching this short mtpbusy active period + */ + for (tries = 2; tries > 0; tries--) { + result = TFA_GET_BF(handle, MTPB); + if (result < 0) { + error = -result; + goto errorExit; + } + status = (unsigned short)result; + + /* check the contents of the STATUS register */ + *ready = (result == 0); + if (*ready) /* if ready go on */ + break; + } + if (tries == 0) /* ready will be 0 if retries exausted */ + return Tfa98xx_Error_Ok; + + /* check the contents of MTP register for non-zero, + * this indicates that the subsys is ready */ + + error = tfa98xx_read_register16(handle, 0x84, &mtp0); + if (error) + goto errorExit; + + *ready = (mtp0 != 0); /* The MTP register written? */ + + return error; + +errorExit: + *ready = 0; + return error; +} + +/* + * The CurrentSense4 register is not in the datasheet, define local + */ +#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1<<2) +#define TFA98XX_CURRENTSENSE4 0x49 +/* + * Disable clock gating + */ +static enum Tfa98xx_Error tfa9890_clockgating(Tfa98xx_handle_t handle, int on) +{ + enum Tfa98xx_Error error; + unsigned short value; + + /* for TFA9890 temporarily disable clock gating when dsp reset is used */ + error = tfa98xx_read_register16(handle, TFA98XX_CURRENTSENSE4, &value); + if (error) return error; + + if (Tfa98xx_Error_Ok == error) { + if (on) /* clock gating on - clear the bit */ + value &= ~TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; + else /* clock gating off - set the bit */ + value |= TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; + + error = tfa98xx_write_register16(handle, TFA98XX_CURRENTSENSE4, value); + } + + return error; +} + +/* + * Tfa9890_DspReset will deal with clock gating control in order + * to reset the DSP for warm state restart + */ +static enum Tfa98xx_Error tfa9890_dsp_reset(Tfa98xx_handle_t handle, int state) +{ + enum Tfa98xx_Error error; + + /* for TFA9890 temporarily disable clock gating + when dsp reset is used */ + tfa9890_clockgating(handle, 0); + + TFA_SET_BF(handle, RST, (uint16_t)state); + + /* clock gating restore */ + error = tfa9890_clockgating(handle, 1); + + return error; +} + +/* + * register device specifics functions + */ +void tfa9890_ops(struct tfa_device_ops *ops) { + ops->tfa_init = tfa9890_specific; + ops->tfa_dsp_reset = tfa9890_dsp_reset; + ops->tfa_dsp_system_stable = tfa9890_dsp_system_stable; +} + diff --git a/sound/soc/codecs/tfa9891/tfa9890_tfafieldnames.h b/sound/soc/codecs/tfa9891/tfa9890_tfafieldnames.h new file mode 100755 index 000000000000..aec03ab53863 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9890_tfafieldnames.h @@ -0,0 +1,76 @@ +/** Filename: Tfa9890_TfaFieldnames.h + * This file was generated automatically on 04/07/15 at 14:46:37. + * Source file: TFA9897N1B_I2C_list_URT_source_v34_90Only.xls + */ + +#define TFA9890_NAMETABLE static tfaBfName_t Tfa9890DatasheetNames[]= {\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input: , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x481, "I2SDOC"}, /* selection data out , */\ + { 0x4a0, "DISP"}, /* idp protection , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x732, "DCMCC"}, /* Max boost coil current - step of 500 mA , */\ + { 0x9c0, "CCFD"}, /* Selection CoolFlux Clock , */\ + { 0x9d0, "ISEL"}, /* selection input 1 or 2 , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0xf00, "VDDD"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "OTDD"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "OVDD"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "UVDD"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "OCDD"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "CLKD"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "DCCD"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "SPKD"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "WDD"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "LCLK"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "INT"}, /* enabling interrupt , */\ + { 0xff0, "INTP"}, /* Setting polarity interupt , */\ + { 0x8f0f, "VERSION"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9890_BITNAMETABLE static tfaBfName_t Tfa9890BitNames[]= {\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input: , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x481, "datao_sel"}, /* selection data out , */\ + { 0x4a0, "disable_idp"}, /* idp protection , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x732, "ctrl_bstcur"}, /* Max boost coil current - step of 500 mA , */\ + { 0x9c0, "sel_cf_clk"}, /* Selection CoolFlux Clock , */\ + { 0x9d0, "intf_sel"}, /* selection input 1 or 2 , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0xf00, "flag_por_mask"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "flag_otpok_mask"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "flag_ovpok_mask"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "flag_uvpok_mask"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "flag_ocp_alarm_mask"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "flag_clocks_stable_mask"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "flag_pwrokbst_mask"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "flag_cf_speakererror_mask"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "flag_watchdog_reset_mask"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "flag_lost_clk_mask"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "enable_interrupt"}, /* enabling interrupt , */\ + { 0xff0, "invert_int_polarity"}, /* Setting polarity interupt , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + diff --git a/sound/soc/codecs/tfa9891/tfa9891_genregs.h b/sound/soc/codecs/tfa9891/tfa9891_genregs.h new file mode 100755 index 000000000000..14f75afc7852 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9891_genregs.h @@ -0,0 +1,1125 @@ +/** Filename: Tfa98xx_genregs.h + * This file was generated automatically on 07/01/15 at 10:25:08. + * Source file: TFA9891_I2C_list_V11.xls + */ + +#ifndef TFA9891_GENREGS_H +#define TFA9891_GENREGS_H + + +#define TFA98XX_STATUSREG 0x00 +#define TFA98XX_BATTERYVOLTAGE 0x01 +#define TFA9891_TEMPERATURE 0x02 +#define TFA98XX_REVISIONNUMBER 0x03 +#define TFA98XX_I2SREG 0x04 +#define TFA98XX_BAT_PROT 0x05 +#define TFA98XX_AUDIO_CTR 0x06 +#define TFA98XX_DCDCBOOST 0x07 +#define TFA98XX_SPKR_CALIBRATION 0x08 +#define TFA98XX_SYS_CTRL 0x09 +#define TFA98XX_I2S_SEL_REG 0x0a +#define TFA98XX_HIDDEN_MTP_KEY2 0x0b +#define TFA98XX_INTERRUPT_REG 0x0f +#define TFA98XX_PDM_CTRL 0x10 +#define TFA98XX_PDM_OUT_CTRL 0x11 +#define TFA98XX_PDM_DS4_R 0x12 +#define TFA98XX_PDM_DS4_L 0x13 +#define TFA98XX_CTRL_SAAM_PGA 0x22 +#define TFA98XX_MISC_CTRL 0x25 +#define TFA98XX_CURRENTSENSE1 0x46 +#define TFA98XX_CURRENTSENSE4 0x49 +#define TFA98XX_HIDDEN_MTP_CTRL_REG3 0x62 +#define TFA9891_CF_CONTROLS 0x70 +#define TFA9891_CF_MAD 0x71 +#define TFA9891_CF_MEM 0x72 +#define TFA9891_CF_STATUS 0x73 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP 0x80 + +/* + * (0x00)-StatusReg + */ + +/* + * POR + */ +#define TFA98XX_STATUSREG_VDDS (0x1<<0) +#define TFA98XX_STATUSREG_VDDS_POS 0 +#define TFA98XX_STATUSREG_VDDS_LEN 1 +#define TFA98XX_STATUSREG_VDDS_MAX 1 +#define TFA98XX_STATUSREG_VDDS_MSK 0x1 + +/* + * PLL_LOCK + */ +#define TFA98XX_STATUSREG_PLLS (0x1<<1) +#define TFA98XX_STATUSREG_PLLS_POS 1 +#define TFA98XX_STATUSREG_PLLS_LEN 1 +#define TFA98XX_STATUSREG_PLLS_MAX 1 +#define TFA98XX_STATUSREG_PLLS_MSK 0x2 + +/* + * flag_otpok + */ +#define TFA98XX_STATUSREG_OTDS (0x1<<2) +#define TFA98XX_STATUSREG_OTDS_POS 2 +#define TFA98XX_STATUSREG_OTDS_LEN 1 +#define TFA98XX_STATUSREG_OTDS_MAX 1 +#define TFA98XX_STATUSREG_OTDS_MSK 0x4 + +/* + * flag_ovpok + */ +#define TFA98XX_STATUSREG_OVDS (0x1<<3) +#define TFA98XX_STATUSREG_OVDS_POS 3 +#define TFA98XX_STATUSREG_OVDS_LEN 1 +#define TFA98XX_STATUSREG_OVDS_MAX 1 +#define TFA98XX_STATUSREG_OVDS_MSK 0x8 + +/* + * flag_uvpok + */ +#define TFA98XX_STATUSREG_UVDS (0x1<<4) +#define TFA98XX_STATUSREG_UVDS_POS 4 +#define TFA98XX_STATUSREG_UVDS_LEN 1 +#define TFA98XX_STATUSREG_UVDS_MAX 1 +#define TFA98XX_STATUSREG_UVDS_MSK 0x10 + +/* + * flag_OCP_alarm + */ +#define TFA98XX_STATUSREG_OCDS (0x1<<5) +#define TFA98XX_STATUSREG_OCDS_POS 5 +#define TFA98XX_STATUSREG_OCDS_LEN 1 +#define TFA98XX_STATUSREG_OCDS_MAX 1 +#define TFA98XX_STATUSREG_OCDS_MSK 0x20 + +/* + * flag_clocks_stable + */ +#define TFA98XX_STATUSREG_CLKS (0x1<<6) +#define TFA98XX_STATUSREG_CLKS_POS 6 +#define TFA98XX_STATUSREG_CLKS_LEN 1 +#define TFA98XX_STATUSREG_CLKS_MAX 1 +#define TFA98XX_STATUSREG_CLKS_MSK 0x40 + +/* + * CLIP + */ +#define TFA98XX_STATUSREG_CLIPS (0x1<<7) +#define TFA98XX_STATUSREG_CLIPS_POS 7 +#define TFA98XX_STATUSREG_CLIPS_LEN 1 +#define TFA98XX_STATUSREG_CLIPS_MAX 1 +#define TFA98XX_STATUSREG_CLIPS_MSK 0x80 + +/* + * mtp_busy + */ +#define TFA98XX_STATUSREG_MTPB (0x1<<8) +#define TFA98XX_STATUSREG_MTPB_POS 8 +#define TFA98XX_STATUSREG_MTPB_LEN 1 +#define TFA98XX_STATUSREG_MTPB_MAX 1 +#define TFA98XX_STATUSREG_MTPB_MSK 0x100 + +/* + * flag_pwrokbst + */ +#define TFA98XX_STATUSREG_DCCS (0x1<<9) +#define TFA98XX_STATUSREG_DCCS_POS 9 +#define TFA98XX_STATUSREG_DCCS_LEN 1 +#define TFA98XX_STATUSREG_DCCS_MAX 1 +#define TFA98XX_STATUSREG_DCCS_MSK 0x200 + +/* + * flag_cf_speakererror + */ +#define TFA98XX_STATUSREG_SPKS (0x1<<10) +#define TFA98XX_STATUSREG_SPKS_POS 10 +#define TFA98XX_STATUSREG_SPKS_LEN 1 +#define TFA98XX_STATUSREG_SPKS_MAX 1 +#define TFA98XX_STATUSREG_SPKS_MSK 0x400 + +/* + * flag_cold_started + */ +#define TFA98XX_STATUSREG_ACS (0x1<<11) +#define TFA98XX_STATUSREG_ACS_POS 11 +#define TFA98XX_STATUSREG_ACS_LEN 1 +#define TFA98XX_STATUSREG_ACS_MAX 1 +#define TFA98XX_STATUSREG_ACS_MSK 0x800 + +/* + * flag_engage + */ +#define TFA98XX_STATUSREG_SWS (0x1<<12) +#define TFA98XX_STATUSREG_SWS_POS 12 +#define TFA98XX_STATUSREG_SWS_LEN 1 +#define TFA98XX_STATUSREG_SWS_MAX 1 +#define TFA98XX_STATUSREG_SWS_MSK 0x1000 + +/* + * flag_watchdog_reset + */ +#define TFA98XX_STATUSREG_WDS (0x1<<13) +#define TFA98XX_STATUSREG_WDS_POS 13 +#define TFA98XX_STATUSREG_WDS_LEN 1 +#define TFA98XX_STATUSREG_WDS_MAX 1 +#define TFA98XX_STATUSREG_WDS_MSK 0x2000 + +/* + * flag_enbl_amp + */ +#define TFA98XX_STATUSREG_AMPS (0x1<<14) +#define TFA98XX_STATUSREG_AMPS_POS 14 +#define TFA98XX_STATUSREG_AMPS_LEN 1 +#define TFA98XX_STATUSREG_AMPS_MAX 1 +#define TFA98XX_STATUSREG_AMPS_MSK 0x4000 + +/* + * flag_enbl_ref + */ +#define TFA98XX_STATUSREG_AREFS (0x1<<15) +#define TFA98XX_STATUSREG_AREFS_POS 15 +#define TFA98XX_STATUSREG_AREFS_LEN 1 +#define TFA98XX_STATUSREG_AREFS_MAX 1 +#define TFA98XX_STATUSREG_AREFS_MSK 0x8000 + +/* + * (0x01)-BatteryVoltage + */ + +/* + * bat_adc + */ +#define TFA98XX_BATTERYVOLTAGE_BATS (0x3ff<<0) +#define TFA98XX_BATTERYVOLTAGE_BATS_POS 0 +#define TFA98XX_BATTERYVOLTAGE_BATS_LEN 10 +#define TFA98XX_BATTERYVOLTAGE_BATS_MAX 1023 +#define TFA98XX_BATTERYVOLTAGE_BATS_MSK 0x3ff + + +/* + * (0x02)-Temperature + */ + +/* + * temp_adc + */ +#define TFA9891_TEMPERATURE_TEMPS (0x1ff<<0) +#define TFA9891_TEMPERATURE_TEMPS_POS 0 +#define TFA9891_TEMPERATURE_TEMPS_LEN 9 +#define TFA9891_TEMPERATURE_TEMPS_MAX 511 +#define TFA9891_TEMPERATURE_TEMPS_MSK 0x1ff + + +/* + * (0x03)-RevisionNumber + */ + +/* + * rev_reg + */ +#define TFA98XX_REVISIONNUMBER_REV (0xff<<0) +#define TFA98XX_REVISIONNUMBER_REV_POS 0 +#define TFA98XX_REVISIONNUMBER_REV_LEN 8 +#define TFA98XX_REVISIONNUMBER_REV_MAX 255 +#define TFA98XX_REVISIONNUMBER_REV_MSK 0xff + + +/* + * (0x04)-I2SReg + */ + +/* + * i2s_seti + */ +#define TFA98XX_I2SREG_I2SF (0x7<<0) +#define TFA98XX_I2SREG_I2SF_POS 0 +#define TFA98XX_I2SREG_I2SF_LEN 3 +#define TFA98XX_I2SREG_I2SF_MAX 7 +#define TFA98XX_I2SREG_I2SF_MSK 0x7 + +/* + * chan_sel1 + */ +#define TFA98XX_I2SREG_CHS12 (0x3<<3) +#define TFA98XX_I2SREG_CHS12_POS 3 +#define TFA98XX_I2SREG_CHS12_LEN 2 +#define TFA98XX_I2SREG_CHS12_MAX 3 +#define TFA98XX_I2SREG_CHS12_MSK 0x18 + +/* + * lr_sw_i2si2 + */ +#define TFA98XX_I2SREG_CHS3 (0x1<<5) +#define TFA98XX_I2SREG_CHS3_POS 5 +#define TFA98XX_I2SREG_CHS3_LEN 1 +#define TFA98XX_I2SREG_CHS3_MAX 1 +#define TFA98XX_I2SREG_CHS3_MSK 0x20 + +/* + * input_sel + */ +#define TFA98XX_I2SREG_CHSA (0x3<<6) +#define TFA98XX_I2SREG_CHSA_POS 6 +#define TFA98XX_I2SREG_CHSA_LEN 2 +#define TFA98XX_I2SREG_CHSA_MAX 3 +#define TFA98XX_I2SREG_CHSA_MSK 0xc0 + +/* + * datao_sel + */ +#define TFA98XX_I2SREG_I2SDOC (0x3<<8) +#define TFA98XX_I2SREG_I2SDOC_POS 8 +#define TFA98XX_I2SREG_I2SDOC_LEN 2 +#define TFA98XX_I2SREG_I2SDOC_MAX 3 +#define TFA98XX_I2SREG_I2SDOC_MSK 0x300 + +/* + * disable_idp + */ +#define TFA98XX_I2SREG_DISP (0x1<<10) +#define TFA98XX_I2SREG_DISP_POS 10 +#define TFA98XX_I2SREG_DISP_LEN 1 +#define TFA98XX_I2SREG_DISP_MAX 1 +#define TFA98XX_I2SREG_DISP_MSK 0x400 + +/* + * enbl_datao + */ +#define TFA98XX_I2SREG_I2SDOE (0x1<<11) +#define TFA98XX_I2SREG_I2SDOE_POS 11 +#define TFA98XX_I2SREG_I2SDOE_LEN 1 +#define TFA98XX_I2SREG_I2SDOE_MAX 1 +#define TFA98XX_I2SREG_I2SDOE_MSK 0x800 + +/* + * i2s_fs + */ +#define TFA98XX_I2SREG_I2SSR (0xf<<12) +#define TFA98XX_I2SREG_I2SSR_POS 12 +#define TFA98XX_I2SREG_I2SSR_LEN 4 +#define TFA98XX_I2SREG_I2SSR_MAX 15 +#define TFA98XX_I2SREG_I2SSR_MSK 0xf000 + + +/* + * (0x05)-bat_prot + */ + +/* + * vbat_prot_attacktime + */ +#define TFA98XX_BAT_PROT_BSSCR (0x3<<0) +#define TFA98XX_BAT_PROT_BSSCR_POS 0 +#define TFA98XX_BAT_PROT_BSSCR_LEN 2 +#define TFA98XX_BAT_PROT_BSSCR_MAX 3 +#define TFA98XX_BAT_PROT_BSSCR_MSK 0x3 + +/* + * vbat_prot_thlevel + */ +#define TFA98XX_BAT_PROT_BSST (0xf<<2) +#define TFA98XX_BAT_PROT_BSST_POS 2 +#define TFA98XX_BAT_PROT_BSST_LEN 4 +#define TFA98XX_BAT_PROT_BSST_MAX 15 +#define TFA98XX_BAT_PROT_BSST_MSK 0x3c + +/* + * vbat_prot_max_reduct + */ +#define TFA98XX_BAT_PROT_BSSRL (0x3<<6) +#define TFA98XX_BAT_PROT_BSSRL_POS 6 +#define TFA98XX_BAT_PROT_BSSRL_LEN 2 +#define TFA98XX_BAT_PROT_BSSRL_MAX 3 +#define TFA98XX_BAT_PROT_BSSRL_MSK 0xc0 + +/* + * vbat_prot_release_t + */ +#define TFA98XX_BAT_PROT_BSSRR (0x7<<8) +#define TFA98XX_BAT_PROT_BSSRR_POS 8 +#define TFA98XX_BAT_PROT_BSSRR_LEN 3 +#define TFA98XX_BAT_PROT_BSSRR_MAX 7 +#define TFA98XX_BAT_PROT_BSSRR_MSK 0x700 + +/* + * vbat_prot_hysterese + */ +#define TFA98XX_BAT_PROT_BSSHY (0x3<<11) +#define TFA98XX_BAT_PROT_BSSHY_POS 11 +#define TFA98XX_BAT_PROT_BSSHY_LEN 2 +#define TFA98XX_BAT_PROT_BSSHY_MAX 3 +#define TFA98XX_BAT_PROT_BSSHY_MSK 0x1800 + +/* + * sel_vbat + */ +#define TFA98XX_BAT_PROT_BSSR (0x1<<14) +#define TFA98XX_BAT_PROT_BSSR_POS 14 +#define TFA98XX_BAT_PROT_BSSR_LEN 1 +#define TFA98XX_BAT_PROT_BSSR_MAX 1 +#define TFA98XX_BAT_PROT_BSSR_MSK 0x4000 + +/* + * bypass_clipper + */ +#define TFA98XX_BAT_PROT_BSSBY (0x1<<15) +#define TFA98XX_BAT_PROT_BSSBY_POS 15 +#define TFA98XX_BAT_PROT_BSSBY_LEN 1 +#define TFA98XX_BAT_PROT_BSSBY_MAX 1 +#define TFA98XX_BAT_PROT_BSSBY_MSK 0x8000 + + +/* + * (0x06)-audio_ctr + */ + +/* + * dpsa + */ +#define TFA98XX_AUDIO_CTR_DPSA (0x1<<0) +#define TFA98XX_AUDIO_CTR_DPSA_POS 0 +#define TFA98XX_AUDIO_CTR_DPSA_LEN 1 +#define TFA98XX_AUDIO_CTR_DPSA_MAX 1 +#define TFA98XX_AUDIO_CTR_DPSA_MSK 0x1 + +/* + * ctrl_slope + */ +#define TFA98XX_AUDIO_CTR_AMPSL (0xf<<1) +#define TFA98XX_AUDIO_CTR_AMPSL_POS 1 +#define TFA98XX_AUDIO_CTR_AMPSL_LEN 4 +#define TFA98XX_AUDIO_CTR_AMPSL_MAX 15 +#define TFA98XX_AUDIO_CTR_AMPSL_MSK 0x1e + +/* + * cf_mute + */ +#define TFA98XX_AUDIO_CTR_CFSM (0x1<<5) +#define TFA98XX_AUDIO_CTR_CFSM_POS 5 +#define TFA98XX_AUDIO_CTR_CFSM_LEN 1 +#define TFA98XX_AUDIO_CTR_CFSM_MAX 1 +#define TFA98XX_AUDIO_CTR_CFSM_MSK 0x20 + +/* + * ctrl_batsensesteepness + */ +#define TFA98XX_AUDIO_CTR_BSSS (0x1<<7) +#define TFA98XX_AUDIO_CTR_BSSS_POS 7 +#define TFA98XX_AUDIO_CTR_BSSS_LEN 1 +#define TFA98XX_AUDIO_CTR_BSSS_MAX 1 +#define TFA98XX_AUDIO_CTR_BSSS_MSK 0x80 + +/* + * vol + */ +#define TFA98XX_AUDIO_CTR_VOL (0xff<<8) +#define TFA98XX_AUDIO_CTR_VOL_POS 8 +#define TFA98XX_AUDIO_CTR_VOL_LEN 8 +#define TFA98XX_AUDIO_CTR_VOL_MAX 255 +#define TFA98XX_AUDIO_CTR_VOL_MSK 0xff00 + + +/* + * (0x07)-DCDCboost + */ + +/* + * ctrl_bstvolt + */ +#define TFA98XX_DCDCBOOST_DCVO (0x7<<0) +#define TFA98XX_DCDCBOOST_DCVO_POS 0 +#define TFA98XX_DCDCBOOST_DCVO_LEN 3 +#define TFA98XX_DCDCBOOST_DCVO_MAX 7 +#define TFA98XX_DCDCBOOST_DCVO_MSK 0x7 + +/* + * ctrl_bstcur + */ +#define TFA98XX_DCDCBOOST_DCMCC (0x7<<3) +#define TFA98XX_DCDCBOOST_DCMCC_POS 3 +#define TFA98XX_DCDCBOOST_DCMCC_LEN 3 +#define TFA98XX_DCDCBOOST_DCMCC_MAX 7 +#define TFA98XX_DCDCBOOST_DCMCC_MSK 0x38 + +/* + * boost_intel + */ +#define TFA98XX_DCDCBOOST_DCIE (0x1<<10) +#define TFA98XX_DCDCBOOST_DCIE_POS 10 +#define TFA98XX_DCDCBOOST_DCIE_LEN 1 +#define TFA98XX_DCDCBOOST_DCIE_MAX 1 +#define TFA98XX_DCDCBOOST_DCIE_MSK 0x400 + +/* + * boost_speed + */ +#define TFA98XX_DCDCBOOST_DCSR (0x1<<11) +#define TFA98XX_DCDCBOOST_DCSR_POS 11 +#define TFA98XX_DCDCBOOST_DCSR_LEN 1 +#define TFA98XX_DCDCBOOST_DCSR_MAX 1 +#define TFA98XX_DCDCBOOST_DCSR_MSK 0x800 + + +/* + * (0x08)-spkr_calibration + */ + +/* + * ext_temp_sel + */ +#define TFA98XX_SPKR_CALIBRATION_TROS (0x1<<0) +#define TFA98XX_SPKR_CALIBRATION_TROS_POS 0 +#define TFA98XX_SPKR_CALIBRATION_TROS_LEN 1 +#define TFA98XX_SPKR_CALIBRATION_TROS_MAX 1 +#define TFA98XX_SPKR_CALIBRATION_TROS_MSK 0x1 + +/* + * ext_temp + */ +#define TFA98XX_SPKR_CALIBRATION_EXTTS (0x1ff<<1) +#define TFA98XX_SPKR_CALIBRATION_EXTTS_POS 1 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_LEN 9 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_MAX 511 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_MSK 0x3fe + + +/* + * (0x09)-sys_ctrl + */ + +/* + * PowerDown + */ +#define TFA98XX_SYS_CTRL_PWDN (0x1<<0) +#define TFA98XX_SYS_CTRL_PWDN_POS 0 +#define TFA98XX_SYS_CTRL_PWDN_LEN 1 +#define TFA98XX_SYS_CTRL_PWDN_MAX 1 +#define TFA98XX_SYS_CTRL_PWDN_MSK 0x1 + +/* + * reset + */ +#define TFA98XX_SYS_CTRL_I2CR (0x1<<1) +#define TFA98XX_SYS_CTRL_I2CR_POS 1 +#define TFA98XX_SYS_CTRL_I2CR_LEN 1 +#define TFA98XX_SYS_CTRL_I2CR_MAX 1 +#define TFA98XX_SYS_CTRL_I2CR_MSK 0x2 + +/* + * enbl_coolflux + */ +#define TFA98XX_SYS_CTRL_CFE (0x1<<2) +#define TFA98XX_SYS_CTRL_CFE_POS 2 +#define TFA98XX_SYS_CTRL_CFE_LEN 1 +#define TFA98XX_SYS_CTRL_CFE_MAX 1 +#define TFA98XX_SYS_CTRL_CFE_MSK 0x4 + +/* + * enbl_amplifier + */ +#define TFA98XX_SYS_CTRL_AMPE (0x1<<3) +#define TFA98XX_SYS_CTRL_AMPE_POS 3 +#define TFA98XX_SYS_CTRL_AMPE_LEN 1 +#define TFA98XX_SYS_CTRL_AMPE_MAX 1 +#define TFA98XX_SYS_CTRL_AMPE_MSK 0x8 + +/* + * enbl_boost + */ +#define TFA98XX_SYS_CTRL_DCA (0x1<<4) +#define TFA98XX_SYS_CTRL_DCA_POS 4 +#define TFA98XX_SYS_CTRL_DCA_LEN 1 +#define TFA98XX_SYS_CTRL_DCA_MAX 1 +#define TFA98XX_SYS_CTRL_DCA_MSK 0x10 + +/* + * cf_configured + */ +#define TFA98XX_SYS_CTRL_SBSL (0x1<<5) +#define TFA98XX_SYS_CTRL_SBSL_POS 5 +#define TFA98XX_SYS_CTRL_SBSL_LEN 1 +#define TFA98XX_SYS_CTRL_SBSL_MAX 1 +#define TFA98XX_SYS_CTRL_SBSL_MSK 0x20 + +/* + * sel_enbl_amplifier + */ +#define TFA98XX_SYS_CTRL_AMPC (0x1<<6) +#define TFA98XX_SYS_CTRL_AMPC_POS 6 +#define TFA98XX_SYS_CTRL_AMPC_LEN 1 +#define TFA98XX_SYS_CTRL_AMPC_MAX 1 +#define TFA98XX_SYS_CTRL_AMPC_MSK 0x40 + +/* + * dcdcoff_mode + */ +#define TFA98XX_SYS_CTRL_DCDIS (0x1<<7) +#define TFA98XX_SYS_CTRL_DCDIS_POS 7 +#define TFA98XX_SYS_CTRL_DCDIS_LEN 1 +#define TFA98XX_SYS_CTRL_DCDIS_MAX 1 +#define TFA98XX_SYS_CTRL_DCDIS_MSK 0x80 + +/* + * cttr_iddqtest + */ +#define TFA98XX_SYS_CTRL_PSDR (0x1<<8) +#define TFA98XX_SYS_CTRL_PSDR_POS 8 +#define TFA98XX_SYS_CTRL_PSDR_LEN 1 +#define TFA98XX_SYS_CTRL_PSDR_MAX 1 +#define TFA98XX_SYS_CTRL_PSDR_MSK 0x100 + +/* + * ctrl_coil_value + */ +#define TFA98XX_SYS_CTRL_DCCV (0x3<<9) +#define TFA98XX_SYS_CTRL_DCCV_POS 9 +#define TFA98XX_SYS_CTRL_DCCV_LEN 2 +#define TFA98XX_SYS_CTRL_DCCV_MAX 3 +#define TFA98XX_SYS_CTRL_DCCV_MSK 0x600 + +/* + * ctrl_sel_cf_clock + */ +#define TFA98XX_SYS_CTRL_CCFD (0x3<<11) +#define TFA98XX_SYS_CTRL_CCFD_POS 11 +#define TFA98XX_SYS_CTRL_CCFD_LEN 2 +#define TFA98XX_SYS_CTRL_CCFD_MAX 3 +#define TFA98XX_SYS_CTRL_CCFD_MSK 0x1800 + +/* + * intf_sel + */ +#define TFA98XX_SYS_CTRL_ISEL (0x1<<13) +#define TFA98XX_SYS_CTRL_ISEL_POS 13 +#define TFA98XX_SYS_CTRL_ISEL_LEN 1 +#define TFA98XX_SYS_CTRL_ISEL_MAX 1 +#define TFA98XX_SYS_CTRL_ISEL_MSK 0x2000 + +/* + * sel_ws_bck + */ +#define TFA98XX_SYS_CTRL_IPLL (0x1<<14) +#define TFA98XX_SYS_CTRL_IPLL_POS 14 +#define TFA98XX_SYS_CTRL_IPLL_LEN 1 +#define TFA98XX_SYS_CTRL_IPLL_MAX 1 +#define TFA98XX_SYS_CTRL_IPLL_MSK 0x4000 + + +/* + * (0x0a)-I2S_sel_reg + */ + +/* + * sel_i2so_l + */ +#define TFA98XX_I2S_SEL_REG_DOLS (0x7<<0) +#define TFA98XX_I2S_SEL_REG_DOLS_POS 0 +#define TFA98XX_I2S_SEL_REG_DOLS_LEN 3 +#define TFA98XX_I2S_SEL_REG_DOLS_MAX 7 +#define TFA98XX_I2S_SEL_REG_DOLS_MSK 0x7 + +/* + * sel_i2so_r + */ +#define TFA98XX_I2S_SEL_REG_DORS (0x7<<3) +#define TFA98XX_I2S_SEL_REG_DORS_POS 3 +#define TFA98XX_I2S_SEL_REG_DORS_LEN 3 +#define TFA98XX_I2S_SEL_REG_DORS_MAX 7 +#define TFA98XX_I2S_SEL_REG_DORS_MSK 0x38 + +/* + * ctrl_spkr_coil + */ +#define TFA98XX_I2S_SEL_REG_SPKL (0x7<<6) +#define TFA98XX_I2S_SEL_REG_SPKL_POS 6 +#define TFA98XX_I2S_SEL_REG_SPKL_LEN 3 +#define TFA98XX_I2S_SEL_REG_SPKL_MAX 7 +#define TFA98XX_I2S_SEL_REG_SPKL_MSK 0x1c0 + +/* + * ctrl_spr_res + */ +#define TFA98XX_I2S_SEL_REG_SPKR (0x3<<9) +#define TFA98XX_I2S_SEL_REG_SPKR_POS 9 +#define TFA98XX_I2S_SEL_REG_SPKR_LEN 2 +#define TFA98XX_I2S_SEL_REG_SPKR_MAX 3 +#define TFA98XX_I2S_SEL_REG_SPKR_MSK 0x600 + +/* + * ctrl_dcdc_spkr_i_comp_gain + */ +#define TFA98XX_I2S_SEL_REG_DCFG (0xf<<11) +#define TFA98XX_I2S_SEL_REG_DCFG_POS 11 +#define TFA98XX_I2S_SEL_REG_DCFG_LEN 4 +#define TFA98XX_I2S_SEL_REG_DCFG_MAX 15 +#define TFA98XX_I2S_SEL_REG_DCFG_MSK 0x7800 + + +/* + * (0x0b)-Hidden_mtp_key2 + */ + +/* + * MTP_key2 + */ +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK (0xff<<0) +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_POS 0 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_LEN 8 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_MAX 255 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_MSK 0xff + + +/* + * (0x0f)-interrupt_reg + */ + +/* + * flag_por_mask + */ +#define TFA98XX_INTERRUPT_REG_VDDD (0x1<<0) +#define TFA98XX_INTERRUPT_REG_VDDD_POS 0 +#define TFA98XX_INTERRUPT_REG_VDDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_VDDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_VDDD_MSK 0x1 + +/* + * flag_otpok_mask + */ +#define TFA98XX_INTERRUPT_REG_OTDD (0x1<<1) +#define TFA98XX_INTERRUPT_REG_OTDD_POS 1 +#define TFA98XX_INTERRUPT_REG_OTDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OTDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OTDD_MSK 0x2 + +/* + * flag_ovpok_mask + */ +#define TFA98XX_INTERRUPT_REG_OVDD (0x1<<2) +#define TFA98XX_INTERRUPT_REG_OVDD_POS 2 +#define TFA98XX_INTERRUPT_REG_OVDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OVDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OVDD_MSK 0x4 + +/* + * flag_uvpok_mask + */ +#define TFA98XX_INTERRUPT_REG_UVDD (0x1<<3) +#define TFA98XX_INTERRUPT_REG_UVDD_POS 3 +#define TFA98XX_INTERRUPT_REG_UVDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_UVDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_UVDD_MSK 0x8 + +/* + * flag_ocp_alarm_mask + */ +#define TFA98XX_INTERRUPT_REG_OCDD (0x1<<4) +#define TFA98XX_INTERRUPT_REG_OCDD_POS 4 +#define TFA98XX_INTERRUPT_REG_OCDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OCDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OCDD_MSK 0x10 + +/* + * flag_clocks_stable_mask + */ +#define TFA98XX_INTERRUPT_REG_CLKD (0x1<<5) +#define TFA98XX_INTERRUPT_REG_CLKD_POS 5 +#define TFA98XX_INTERRUPT_REG_CLKD_LEN 1 +#define TFA98XX_INTERRUPT_REG_CLKD_MAX 1 +#define TFA98XX_INTERRUPT_REG_CLKD_MSK 0x20 + +/* + * flag_pwrokbst_mask + */ +#define TFA98XX_INTERRUPT_REG_DCCD (0x1<<6) +#define TFA98XX_INTERRUPT_REG_DCCD_POS 6 +#define TFA98XX_INTERRUPT_REG_DCCD_LEN 1 +#define TFA98XX_INTERRUPT_REG_DCCD_MAX 1 +#define TFA98XX_INTERRUPT_REG_DCCD_MSK 0x40 + +/* + * flag_cf_speakererror_mask + */ +#define TFA98XX_INTERRUPT_REG_SPKD (0x1<<7) +#define TFA98XX_INTERRUPT_REG_SPKD_POS 7 +#define TFA98XX_INTERRUPT_REG_SPKD_LEN 1 +#define TFA98XX_INTERRUPT_REG_SPKD_MAX 1 +#define TFA98XX_INTERRUPT_REG_SPKD_MSK 0x80 + +/* + * flag_watchdog_reset_mask + */ +#define TFA98XX_INTERRUPT_REG_WDD (0x1<<8) +#define TFA98XX_INTERRUPT_REG_WDD_POS 8 +#define TFA98XX_INTERRUPT_REG_WDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_WDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_WDD_MSK 0x100 + +/* + * enable_interrupt + */ +#define TFA98XX_INTERRUPT_REG_INT (0x1<<14) +#define TFA98XX_INTERRUPT_REG_INT_POS 14 +#define TFA98XX_INTERRUPT_REG_INT_LEN 1 +#define TFA98XX_INTERRUPT_REG_INT_MAX 1 +#define TFA98XX_INTERRUPT_REG_INT_MSK 0x4000 + +/* + * invert_int_polarity + */ +#define TFA98XX_INTERRUPT_REG_INTP (0x1<<15) +#define TFA98XX_INTERRUPT_REG_INTP_POS 15 +#define TFA98XX_INTERRUPT_REG_INTP_LEN 1 +#define TFA98XX_INTERRUPT_REG_INTP_MAX 1 +#define TFA98XX_INTERRUPT_REG_INTP_MSK 0x8000 + + +/* + * (0x10)-pdm_ctrl + */ + +/* + * pdm_i2s_input + */ +#define TFA98XX_PDM_CTRL_PDMSEL (0x1<<0) +#define TFA98XX_PDM_CTRL_PDMSEL_POS 0 +#define TFA98XX_PDM_CTRL_PDMSEL_LEN 1 +#define TFA98XX_PDM_CTRL_PDMSEL_MAX 1 +#define TFA98XX_PDM_CTRL_PDMSEL_MSK 0x1 + +/* + * I2S_master_ena + */ +#define TFA98XX_PDM_CTRL_I2SMOUTEN (0x1<<1) +#define TFA98XX_PDM_CTRL_I2SMOUTEN_POS 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_LEN 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_MAX 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_MSK 0x2 + +/* + * pdm_out_sel_r + */ +#define TFA98XX_PDM_CTRL_PDMORSEL (0x3<<2) +#define TFA98XX_PDM_CTRL_PDMORSEL_POS 2 +#define TFA98XX_PDM_CTRL_PDMORSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PDMORSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PDMORSEL_MSK 0xc + +/* + * pdm_out_sel_l + */ +#define TFA98XX_PDM_CTRL_PDMOLSEL (0x3<<4) +#define TFA98XX_PDM_CTRL_PDMOLSEL_POS 4 +#define TFA98XX_PDM_CTRL_PDMOLSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PDMOLSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PDMOLSEL_MSK 0x30 + +/* + * micdat_out_sel + */ +#define TFA98XX_PDM_CTRL_PADSEL (0x3<<6) +#define TFA98XX_PDM_CTRL_PADSEL_POS 6 +#define TFA98XX_PDM_CTRL_PADSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PADSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PADSEL_MSK 0xc0 + + +/* + * (0x11)-pdm_out_ctrl + */ + +/* + * secure_dly + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN (0x1<<0) +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_POS 0 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_MSK 0x1 + +/* + * d_out_valid_rf_mux + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF (0x1<<1) +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_POS 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_MSK 0x2 + +/* + * Speak_As_Mic_en + */ +#define TFA98XX_PDM_OUT_CTRL_SAAMEN (0x1<<4) +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_POS 4 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_MSK 0x10 + +/* + * speak_as_mic_lp_mode + */ +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN (0x1<<5) +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_POS 5 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_MSK 0x20 + +/* + * pdm_out_rate + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN (0x1<<6) +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_POS 6 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_MSK 0x40 + + +/* + * (0x12)-pdm_ds4_r + */ + +/* + * ds4_g1_r + */ +#define TFA98XX_PDM_DS4_R_PDMORG1 (0xf<<0) +#define TFA98XX_PDM_DS4_R_PDMORG1_POS 0 +#define TFA98XX_PDM_DS4_R_PDMORG1_LEN 4 +#define TFA98XX_PDM_DS4_R_PDMORG1_MAX 15 +#define TFA98XX_PDM_DS4_R_PDMORG1_MSK 0xf + +/* + * ds4_g2_r + */ +#define TFA98XX_PDM_DS4_R_PDMORG2 (0xf<<4) +#define TFA98XX_PDM_DS4_R_PDMORG2_POS 4 +#define TFA98XX_PDM_DS4_R_PDMORG2_LEN 4 +#define TFA98XX_PDM_DS4_R_PDMORG2_MAX 15 +#define TFA98XX_PDM_DS4_R_PDMORG2_MSK 0xf0 + + +/* + * (0x13)-pdm_ds4_l + */ + +/* + * ds4_g1_l + */ +#define TFA98XX_PDM_DS4_L_PDMOLG1 (0xf<<0) +#define TFA98XX_PDM_DS4_L_PDMOLG1_POS 0 +#define TFA98XX_PDM_DS4_L_PDMOLG1_LEN 4 +#define TFA98XX_PDM_DS4_L_PDMOLG1_MAX 15 +#define TFA98XX_PDM_DS4_L_PDMOLG1_MSK 0xf + +/* + * ds4_g2_l + */ +#define TFA98XX_PDM_DS4_L_PDMOLG2 (0xf<<4) +#define TFA98XX_PDM_DS4_L_PDMOLG2_POS 4 +#define TFA98XX_PDM_DS4_L_PDMOLG2_LEN 4 +#define TFA98XX_PDM_DS4_L_PDMOLG2_MAX 15 +#define TFA98XX_PDM_DS4_L_PDMOLG2_MSK 0xf0 + + +/* + * (0x22)-ctrl_saam_pga + */ + +/* + * Ctrl_saam_pga_gain + */ +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN (0x7<<0) +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS 0 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_LEN 3 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MAX 7 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK 0x7 + +/* + * ctrl_saam_pga_src + */ +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL (0x1<<5) +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_POS 5 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_LEN 1 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_MAX 1 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_MSK 0x20 + + +/* + * (0x25)-misc_ctrl + */ + +/* + * pll_fcco + */ +#define TFA98XX_MISC_CTRL_PLLCCOSEL (0x1<<0) +#define TFA98XX_MISC_CTRL_PLLCCOSEL_POS 0 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_LEN 1 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_MAX 1 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_MSK 0x1 + + +/* + * (0x46)-CurrentSense1 + */ + +/* + * bypass_gc + */ +#define TFA98XX_CURRENTSENSE1_CSBYPGC (0x1<<0) +#define TFA98XX_CURRENTSENSE1_CSBYPGC_POS 0 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_LEN 1 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_MAX 1 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_MSK 0x1 + + +/* + * (0x49)-CurrentSense4 + */ + +/* + * ctrl_bypassclip + */ +#define TFA98XX_CURRENTSENSE4_CLIP (0x1<<0) +#define TFA98XX_CURRENTSENSE4_CLIP_POS 0 +#define TFA98XX_CURRENTSENSE4_CLIP_LEN 1 +#define TFA98XX_CURRENTSENSE4_CLIP_MAX 1 +#define TFA98XX_CURRENTSENSE4_CLIP_MSK 0x1 + +/* + * ctrl_bypassclip2 + */ +#define TFA98XX_CURRENTSENSE4_CLIP2 (0x1<<1) +#define TFA98XX_CURRENTSENSE4_CLIP2_POS 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_LEN 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_MAX 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_MSK 0x2 + + +/* + * (0x62)-Hidden_mtp_ctrl_reg3 + */ + + +/* + * (0x70)-cf_controls + */ + +/* + * cf_rst_dsp + */ +#define TFA98XX_CF_CONTROLS_RST (0x1<<0) +#define TFA98XX_CF_CONTROLS_RST_POS 0 +#define TFA98XX_CF_CONTROLS_RST_LEN 1 +#define TFA98XX_CF_CONTROLS_RST_MAX 1 +#define TFA98XX_CF_CONTROLS_RST_MSK 0x1 + +/* + * cf_dmem + */ +#define TFA98XX_CF_CONTROLS_DMEM (0x3<<1) +#define TFA98XX_CF_CONTROLS_DMEM_POS 1 +#define TFA98XX_CF_CONTROLS_DMEM_LEN 2 +#define TFA98XX_CF_CONTROLS_DMEM_MAX 3 +#define TFA98XX_CF_CONTROLS_DMEM_MSK 0x6 + +/* + * cf_aif + */ +#define TFA98XX_CF_CONTROLS_AIF (0x1<<3) +#define TFA98XX_CF_CONTROLS_AIF_POS 3 +#define TFA98XX_CF_CONTROLS_AIF_LEN 1 +#define TFA98XX_CF_CONTROLS_AIF_MAX 1 +#define TFA98XX_CF_CONTROLS_AIF_MSK 0x8 + +/* + * cf_int + */ +#define TFA98XX_CF_CONTROLS_CFINT (0x1<<4) +#define TFA98XX_CF_CONTROLS_CFINT_POS 4 +#define TFA98XX_CF_CONTROLS_CFINT_LEN 1 +#define TFA98XX_CF_CONTROLS_CFINT_MAX 1 +#define TFA98XX_CF_CONTROLS_CFINT_MSK 0x10 + +/* + * cf_req + */ +#define TFA98XX_CF_CONTROLS_REQ (0xff<<8) +#define TFA98XX_CF_CONTROLS_REQ_POS 8 +#define TFA98XX_CF_CONTROLS_REQ_LEN 8 +#define TFA98XX_CF_CONTROLS_REQ_MAX 255 +#define TFA98XX_CF_CONTROLS_REQ_MSK 0xff00 + + +/* + * (0x71)-cf_mad + */ + +/* + * cf_madd + */ +#define TFA9891_CF_MAD_MADD (0xffff<<0) +#define TFA9891_CF_MAD_MADD_POS 0 +#define TFA9891_CF_MAD_MADD_LEN 16 +#define TFA9891_CF_MAD_MADD_MAX 65535 +#define TFA9891_CF_MAD_MADD_MSK 0xffff + + +/* + * (0x72)-cf_mem + */ + +/* + * cf_mema + */ +#define TFA9891_CF_MEM_MEMA (0xffff<<0) +#define TFA9891_CF_MEM_MEMA_POS 0 +#define TFA9891_CF_MEM_MEMA_LEN 16 +#define TFA9891_CF_MEM_MEMA_MAX 65535 +#define TFA9891_CF_MEM_MEMA_MSK 0xffff + + +/* + * (0x73)-cf_status + */ + +/* + * cf_err + */ +#define TFA9891_CF_STATUS_ERR (0xff<<0) +#define TFA9891_CF_STATUS_ERR_POS 0 +#define TFA9891_CF_STATUS_ERR_LEN 8 +#define TFA9891_CF_STATUS_ERR_MAX 255 +#define TFA9891_CF_STATUS_ERR_MSK 0xff + +/* + * cf_ack + */ +#define TFA9891_CF_STATUS_ACK (0xff<<8) +#define TFA9891_CF_STATUS_ACK_POS 8 +#define TFA9891_CF_STATUS_ACK_LEN 8 +#define TFA9891_CF_STATUS_ACK_MAX 255 +#define TFA9891_CF_STATUS_ACK_MSK 0xff00 + + +/* + * (0x80)-Key2Protected_spkr_cal_mtp + */ + +/* + * calibration_onetime + */ +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC (0x1<<0) +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_POS 0 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_LEN 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_MAX 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_MSK 0x1 + +/* + * calibr_ron_done + */ +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX (0x1<<1) +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_POS 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_LEN 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_MAX 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_MSK 0x2 + +#endif /* TFA9891_GENREGS_H */ diff --git a/sound/soc/codecs/tfa9891/tfa9891_init.c b/sound/soc/codecs/tfa9891/tfa9891_init.c new file mode 100755 index 000000000000..bce1db9440c3 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9891_init.c @@ -0,0 +1,51 @@ +/* + *Copyright 2014,2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + +static enum Tfa98xx_Error tfa9891_specific(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* ----- generated code start ----- */ + /* ----- version 18.0 ----- */ + tfa98xx_write_register16(handle, 0x09, 0x025d); //POR=0x024d + tfa98xx_write_register16(handle, 0x10, 0x0018); //POR=0x0024 + tfa98xx_write_register16(handle, 0x22, 0x0003); //POR=0x0023 + tfa98xx_write_register16(handle, 0x25, 0x0001); //POR=0x0000 + tfa98xx_write_register16(handle, 0x46, 0x0000); //POR=0x4000 + tfa98xx_write_register16(handle, 0x55, 0x3ffb); //POR=0x7fff + /* ----- generated code end ----- */ + + return error; +} + + +/* + * register device specifics functions + */ +void tfa9891_ops(struct tfa_device_ops *ops) { + ops->tfa_init=tfa9891_specific; + +} diff --git a/sound/soc/codecs/tfa9891/tfa9891_tfafieldnames.h b/sound/soc/codecs/tfa9891/tfa9891_tfafieldnames.h new file mode 100755 index 000000000000..987db1bf3013 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9891_tfafieldnames.h @@ -0,0 +1,515 @@ +/* + * tfa9891_tfafieldnames.h + * + * Created on: Jul 16, 2015 + * Author: wim + */ + +#ifndef TFA_INC_TFA9891_TFAFIELDNAMES_H_ +#define TFA_INC_TFA9891_TFAFIELDNAMES_H_ + +/** Filename: Tfa9891_TfaFieldnames.h + * This file was generated automatically on 07/16/15 at 15:00:02. + * Source file: TFA9891_I2C_list_V13.xls + */ + +#define TFA9891_I2CVERSION 13.0 + + +#define TFA9891_NAMETABLE static tfaBfName_t Tfa9891DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* POR , */\ + { 0x10, "PLLS"}, /* PLL , */\ + { 0x20, "OTDS"}, /* OTP , */\ + { 0x30, "OVDS"}, /* OVP , */\ + { 0x40, "UVDS"}, /* UVP , */\ + { 0x50, "OCDS"}, /* OCP , */\ + { 0x60, "CLKS"}, /* Clocks , */\ + { 0x70, "CLIPS"}, /* CLIP , */\ + { 0x80, "MTPB"}, /* MTP , */\ + { 0x90, "DCCS"}, /* BOOST , */\ + { 0xa0, "SPKS"}, /* Speaker , */\ + { 0xb0, "ACS"}, /* cold start flag , */\ + { 0xc0, "SWS"}, /* flag engage , */\ + { 0xd0, "WDS"}, /* flag watchdog reset , */\ + { 0xe0, "AMPS"}, /* amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* references are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage readout; 0[V]..5.5[V] , */\ + { 0x208, "TEMPS"}, /* Temperature readout , */\ + { 0x307, "REV"}, /* Device Revision , */\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* Channel Selection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x481, "I2SDOC"}, /* Selection for I2S data out , */\ + { 0x4a0, "DISP"}, /* idp protection , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x501, "BSSCR"}, /* ProtectionAttackTime , */\ + { 0x523, "BSST"}, /* ProtectionThreshold , */\ + { 0x561, "BSSRL"}, /* ProtectionMaximumReduction , */\ + { 0x582, "BSSRR"}, /* Protection Release Timer , */\ + { 0x5b1, "BSSHY"}, /* ProtectionHysterese , */\ + { 0x5e0, "BSSR"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "BSSBY"}, /* bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation , */\ + { 0x613, "AMPSL"}, /* control slope , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "BSSS"}, /* batsensesteepness , */\ + { 0x687, "VOL"}, /* volume control (in CoolFlux) , */\ + { 0x702, "DCVO"}, /* Boost voltage , */\ + { 0x732, "DCMCC"}, /* Max boost coil current , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x800, "TROS"}, /* select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "EXTTS"}, /* external temperature setting to be given by host , */\ + { 0x900, "PWDN"}, /* ON/OFF , */\ + { 0x910, "I2CR"}, /* I2CReset , */\ + { 0x920, "CFE"}, /* EnableCoolFlux , */\ + { 0x930, "AMPE"}, /* EnableAmplifier , */\ + { 0x940, "DCA"}, /* EnableBoost , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection on how AmplifierEnabling , */\ + { 0x970, "DCDIS"}, /* DCDC not connected , */\ + { 0x980, "PSDR"}, /* Iddq test amplifier , */\ + { 0x991, "DCCV"}, /* Coil Value , */\ + { 0x9b1, "CCFD"}, /* Selection CoolFluxClock , */\ + { 0x9d0, "ISEL"}, /* Interface Selection , */\ + { 0x9e0, "IPLL"}, /* selection input PLL for lock , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0xb07, "MTPK"}, /* MTP KEY2 register , */\ + { 0xf00, "VDDD"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "OTDD"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "OVDD"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "UVDD"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "OCDD"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "CLKD"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "DCCD"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "SPKD"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "WDD"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xfe0, "INT"}, /* enabling interrupt , */\ + { 0xff0, "INTP"}, /* Setting polarity interupt , */\ + { 0x1000, "PDMSEL"}, /* Audio input interface mode , */\ + { 0x1010, "I2SMOUTEN"}, /* I2S Master enable (CLK and WS pads) , */\ + { 0x1021, "PDMORSEL"}, /* PDM Output right channel source selection , */\ + { 0x1041, "PDMOLSEL"}, /* PDM Output Left/Mono channel source selection , */\ + { 0x1061, "PADSEL"}, /* Output interface mode and ball selection , */\ + { 0x1100, "PDMOSDEN"}, /* Secure delay Cell , */\ + { 0x1110, "PDMOSDCF"}, /* Rising Falling Resync control Mux , */\ + { 0x1140, "SAAMEN"}, /* Speaker As a Mic feature ON/OFF , */\ + { 0x1150, "SAAMLPEN"}, /* speaker_as_mic low power mode (only in PDM_out mode), */\ + { 0x1160, "PDMOINTEN"}, /* PDM output interpolation ratio , */\ + { 0x1203, "PDMORG1"}, /* PDM Interpolator Right Channel DS4 G1 Gain Value , */\ + { 0x1243, "PDMORG2"}, /* PDM Interpolator Right Channel DS4 G2 Gain Value , */\ + { 0x1303, "PDMOLG1"}, /* PDM Interpolator Left Channel DS4 G1 Gain Value , */\ + { 0x1343, "PDMOLG2"}, /* PDM Interpolator Left Channel DS4 G2 Gain Value , */\ + { 0x2202, "SAAMGAIN"}, /* pga gain , */\ + { 0x2250, "SAAMPGACTRL"}, /* 0 = active input common mode voltage source at the attenuator/PGA level, */\ + { 0x2500, "PLLCCOSEL"}, /* pll cco frequency , */\ + { 0x4600, "CSBYPGC"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4900, "CLIP"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4910, "CLIP2"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x62b0, "CIMTP"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "CFINT"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "REQ"}, /* request for access (8 channels) , */\ + { 0x710f, "MADD"}, /* memory-address to be accessed , */\ + { 0x720f, "MEMA"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "ERR"}, /* cf error Flags , */\ + { 0x7387, "ACK"}, /* acknowledge of requests (8 channels")" , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "MTPEX"}, /* (key2 protected) calibration of Ron has been executed, */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9891_BITNAMETABLE static tfaBfName_t Tfa9891BitNames[]= {\ + { 0x0, "POR"}, /* POR , */\ + { 0x10, "PLL_LOCK"}, /* PLL , */\ + { 0x20, "flag_otpok"}, /* OTP , */\ + { 0x30, "flag_ovpok"}, /* OVP , */\ + { 0x40, "flag_uvpok"}, /* UVP , */\ + { 0x50, "flag_OCP_alarm"}, /* OCP , */\ + { 0x60, "flag_clocks_stable"}, /* Clocks , */\ + { 0x70, "CLIP"}, /* CLIP , */\ + { 0x80, "mtp_busy"}, /* MTP , */\ + { 0x90, "flag_pwrokbst"}, /* BOOST , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker , */\ + { 0xb0, "flag_cold_started"}, /* cold start flag , */\ + { 0xc0, "flag_engage"}, /* flag engage , */\ + { 0xd0, "flag_watchdog_reset"}, /* flag watchdog reset , */\ + { 0xe0, "flag_enbl_amp"}, /* amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* references are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage readout; 0[V]..5.5[V] , */\ + { 0x208, "temp_adc"}, /* Temperature readout , */\ + { 0x307, "rev_reg"}, /* Device Revision , */\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* Channel Selection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x481, "datao_sel"}, /* Selection for I2S data out , */\ + { 0x4a0, "disable_idp"}, /* idp protection , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* ProtectionAttackTime , */\ + { 0x523, "vbat_prot_thlevel"}, /* ProtectionThreshold , */\ + { 0x561, "vbat_prot_max_reduct"}, /* ProtectionMaximumReduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Protection Release Timer , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* ProtectionHysterese , */\ + { 0x5d0, "reset_min_vbat"}, /* reset clipper , */\ + { 0x5e0, "sel_vbat"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "bypass_clipper"}, /* bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation , */\ + { 0x613, "ctrl_slope"}, /* control slope , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x660, "sel_other_vamp"}, /* Input selection for the second channel of the DCDC inteligent mode detector, */\ + { 0x670, "ctrl_batsensesteepness"}, /* batsensesteepness , */\ + { 0x687, "vol"}, /* volume control (in CoolFlux) , */\ + { 0x702, "ctrl_bstvolt"}, /* Boost voltage , */\ + { 0x732, "ctrl_bstcur"}, /* Max boost coil current , */\ + { 0x761, "ctrl_slopebst_1_0"}, /* Setting for the slope of the boost converter power stage, */\ + { 0x781, "ctrl_slopebst_3_2"}, /* Setting for the part of the power transistor voltage to be used in peak current mode control, */\ + { 0x7a0, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7b0, "boost_speed"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c1, "ctrl_delay_comp_dcdc"}, /* delay compensation in current patg compared to delay in the audio path (relative) , */\ + { 0x7e0, "boost_input"}, /* Selection intelligent boost detector input , */\ + { 0x7f0, "ctrl_supplysense"}, /* ADC10 input selection , */\ + { 0x800, "ext_temp_sel"}, /* select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "ext_temp"}, /* external temperature setting to be given by host , */\ + { 0x8a0, "ctrl_spk_coilpvp_bst"}, /* Peak voltage protection boost converter , */\ + { 0x8b2, "ctrl_dcdc_synchronisation"}, /* DCDC synchronisation off + 7 positions , */\ + { 0x8e0, "ctrl_cs_samplevalid"}, /* sample valid moment for CS in single sample moment mode, */\ + { 0x900, "PowerDown"}, /* ON/OFF , */\ + { 0x910, "reset"}, /* I2CReset , */\ + { 0x920, "enbl_coolflux"}, /* EnableCoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* EnableAmplifier , */\ + { 0x940, "enbl_boost"}, /* EnableBoost , */\ + { 0x950, "cf_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection on how AmplifierEnabling , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC not connected , */\ + { 0x980, "cttr_iddqtest"}, /* Iddq test amplifier , */\ + { 0x991, "ctrl_coil_value"}, /* Coil Value , */\ + { 0x9b1, "ctrl_sel_cf_clock"}, /* Selection CoolFluxClock , */\ + { 0x9d0, "intf_sel"}, /* Interface Selection , */\ + { 0x9e0, "sel_ws_bck"}, /* selection input PLL for lock , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0xb07, "MTP_key2"}, /* MTP KEY2 register , */\ + { 0xc0c, "clk_sync_delay"}, /* Delay count for clock synchronisation , */\ + { 0xcf0, "enbl_clk_sync"}, /* Enable CGU clock synchronisation , */\ + { 0xd0c, "adc_sync_delay"}, /* Delay count for ADC synchronisation , */\ + { 0xdf0, "enable_adc_sync"}, /* Enable ADC synchronisation , */\ + { 0xe00, "bypass_dcdc_curr_prot"}, /* to switch off dcdc reduction with bat prot , */\ + { 0xe24, "ctrl_digtoana6_2"}, /* for extra connections digital to analog , */\ + { 0xe70, "switch_on_icomp"}, /* icomp dem switch , */\ + { 0xe87, "reserve_reg_1_7_0"}, /* reserved , */\ + { 0xf00, "flag_por_mask"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "flag_otpok_mask"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "flag_ovpok_mask"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "flag_uvpok_mask"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "flag_ocp_alarm_mask"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "flag_clocks_stable_mask"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "flag_pwrokbst_mask"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "flag_cf_speakererror_mask"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "flag_watchdog_reset_mask"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "flag_lost_clk_mask"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "enable_interrupt"}, /* enabling interrupt , */\ + { 0xff0, "invert_int_polarity"}, /* Setting polarity interupt , */\ + { 0x1000, "pdm_i2s_input"}, /* Audio input interface mode , */\ + { 0x1010, "I2S_master_ena"}, /* I2S Master enable (CLK and WS pads) , */\ + { 0x1021, "pdm_out_sel_r"}, /* PDM Output right channel source selection , */\ + { 0x1041, "pdm_out_sel_l"}, /* PDM Output Left/Mono channel source selection , */\ + { 0x1061, "micdat_out_sel"}, /* Output interface mode and ball selection , */\ + { 0x1100, "secure_dly"}, /* Secure delay Cell , */\ + { 0x1110, "d_out_valid_rf_mux"}, /* Rising Falling Resync control Mux , */\ + { 0x1140, "Speak_As_Mic_en"}, /* Speaker As a Mic feature ON/OFF , */\ + { 0x1150, "speak_as_mic_lp_mode"}, /* speaker_as_mic low power mode (only in PDM_out mode), */\ + { 0x1160, "pdm_out_rate"}, /* PDM output interpolation ratio , */\ + { 0x1203, "ds4_g1_r"}, /* PDM Interpolator Right Channel DS4 G1 Gain Value , */\ + { 0x1243, "ds4_g2_r"}, /* PDM Interpolator Right Channel DS4 G2 Gain Value , */\ + { 0x1303, "ds4_g1_l"}, /* PDM Interpolator Left Channel DS4 G1 Gain Value , */\ + { 0x1343, "ds4_g2_l"}, /* PDM Interpolator Left Channel DS4 G2 Gain Value , */\ + { 0x1400, "clk_secure_dly"}, /* Secure delay Cell on clock path , */\ + { 0x1410, "data_secure_dly"}, /* Secure delay Cell enable on PDM data path , */\ + { 0x2202, "Ctrl_saam_pga_gain"}, /* pga gain , */\ + { 0x2250, "ctrl_saam_pga_src"}, /* 0 = active input common mode voltage source at the attenuator/PGA level, */\ + { 0x2300, "flag_saam_spare"}, /* spare flag , */\ + { 0x2400, "ctrl_saam_pga_tm"}, /* enables PGA test mode , */\ + { 0x2500, "pll_fcco"}, /* pll cco frequency , */\ + { 0x3000, "flag_hi_small"}, /* positive small window dcdc converter , */\ + { 0x3010, "flag_hi_large"}, /* positive large window dcdc converter , */\ + { 0x3020, "flag_lo_small"}, /* negative small window dcdc converter , */\ + { 0x3030, "flag_lo_large"}, /* negative large window dcdc converter , */\ + { 0x3040, "flag_voutcomp"}, /* flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3050, "flag_voutcomp93"}, /* flag_voutcomp93, indication Vset is larger than 1.07* Vbat , */\ + { 0x3060, "flag_voutcomp86"}, /* flag_voutcomp86, indication Vset is larger than 1.14* Vbat , */\ + { 0x3070, "flag_hiz"}, /* flag_hiz, indication Vbst is larger than Vbat , */\ + { 0x3080, "flag_hi_peak"}, /* flag_hi_peak, indication hi_peak , */\ + { 0x3090, "flag_ocpokbst"}, /* flag_ocpokbst, indication no over current in boost converter pmos switch, */\ + { 0x30a0, "flag_peakcur"}, /* flag_peakcur, indication current is max in dcdc converter, */\ + { 0x30b0, "flag_ocpokap"}, /* flag_ocpokap, indication no over current in amplifier "a" pmos output stage, */\ + { 0x30c0, "flag_ocpokan"}, /* flag_ocpokan, indication no over current in amplifier "a" nmos output stage, */\ + { 0x30d0, "flag_ocpokbp"}, /* flag_ocpokbp, indication no over current in amplifier "b" pmos output stage, */\ + { 0x30e0, "flag_ocpokbn"}, /* flag_ocpokbn, indication no over current in amplifier"b" nmos output stage, */\ + { 0x30f0, "lost_clk"}, /* lost_clk, lost clock indication CGU , */\ + { 0x310f, "mtp_man_data_out"}, /* single word read from MTP (manual copy) , */\ + { 0x3200, "key01_locked"}, /* key01_locked, indication key 1 is locked , */\ + { 0x3210, "key02_locked"}, /* key02_locked, indication key 2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* mtp_ecc_tcout , */\ + { 0x3280, "mtpctrl_valid_test_rd"}, /* mtp test readout for read , */\ + { 0x3290, "mtpctrl_valid_test_wr"}, /* mtp test readout for write , */\ + { 0x32a0, "flag_in_alarm_state"}, /* alarm state , */\ + { 0x32b0, "mtp_ecc_err2"}, /* two or more bit errors detected in MTP, can not reconstruct value, */\ + { 0x32c0, "mtp_ecc_err1"}, /* one bit error detected in MTP, reconstructed value, */\ + { 0x32d0, "mtp_mtp_hvf"}, /* high voltage ready flag for MTP , */\ + { 0x32f0, "mtp_zero_check_fail"}, /* zero check failed (tbd) for MTP , */\ + { 0x3300, "flag_adc10_ready"}, /* flag_adc10_ready, indication adc10 is ready , */\ + { 0x3310, "flag_clipa_high"}, /* flag_clipa_high, indication pmos amplifier "a" is clipping, */\ + { 0x3320, "flag_clipa_low"}, /* flag_clipa_low, indication nmos amplifier "a" is clipping, */\ + { 0x3330, "flag_clipb_high"}, /* flag_clipb_high, indication pmos amplifier "b" is clipping, */\ + { 0x3340, "flag_clipb_low"}, /* flag_clipb_low, indication nmos amplifier "b" is clipping, */\ + { 0x3359, "data_adc10_tempbat"}, /* adc 10 data output for testing , */\ + { 0x33f0, "flag_vddd_comp_nok"}, /* power switch flag 2 for testing , */\ + { 0x400f, "hid_code"}, /* hidden code , */\ + { 0x4100, "bypass_hp"}, /* Bypass_High Pass Filter , */\ + { 0x4110, "hard_mute"}, /* Hard Mute , */\ + { 0x4120, "soft_mute"}, /* Soft Mute , */\ + { 0x4134, "PWM_Delay"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWM_Shape"}, /* PWM Shape , */\ + { 0x4190, "PWM_BitLength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4207, "ctrl_drive"}, /* drive bits to select amount of power stages amplifier, */\ + { 0x4281, "dpsalevel"}, /* DPSA Threshold level , */\ + { 0x42a1, "dpsa_release"}, /* DPSA Release time , */\ + { 0x42c0, "ctrl_coincidence"}, /* Prevent simultaneously switching of output stage , */\ + { 0x42d0, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x42e0, "ctrl_test_sdeltaoffset"}, /* ctrl_test_sdeltaoffset , */\ + { 0x42f0, "ctrl_test_sdeltaclk"}, /* ctrl_test_sdeltaclk , */\ + { 0x4309, "ctrl_drivebst"}, /* Drive bits to select the powertransistor sections boost converter, */\ + { 0x43a0, "ctrl_ocptestbst"}, /* Boost OCP. , */\ + { 0x43c0, "enbl_hi_peak"}, /* enable for high peak comparator , */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT coolflux , */\ + { 0x43e0, "ctrl_sensetest_amp"}, /* sensetest amplifier , */\ + { 0x43f0, "test_bcontrol"}, /* test _bcontrol , */\ + { 0x4400, "ctrl_reversebst"}, /* OverCurrent Protection selection of power stage boost converter, */\ + { 0x4410, "ctrl_sensetest"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0x4420, "enbl_engagebst"}, /* Enable power stage dcdc controller , */\ + { 0x4430, "enbl_hi_small"}, /* Enable bit of hi (small) comparator , */\ + { 0x4440, "enbl_hi_large"}, /* Enable bit of hi (large) comparator , */\ + { 0x4450, "enbl_lo_small"}, /* Enable bit of lo (small) comparator , */\ + { 0x4460, "enbl_lo_large"}, /* Enable bit of lo (large) comparator , */\ + { 0x4470, "enbl_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x4480, "enbl_voutcomp"}, /* Enable vout comparators , */\ + { 0x4490, "enbl_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x44a0, "enbl_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x44b0, "enbl_hizcom"}, /* Enable hiz comparator , */\ + { 0x44c0, "enbl_pcdac"}, /* Enable peak current dac , */\ + { 0x44d0, "enbl_pccomp"}, /* Enable peak current comparator , */\ + { 0x44e0, "enbl_windac"}, /* Enable window dac , */\ + { 0x44f0, "enbl_powerbst"}, /* Enable line of the powerstage , */\ + { 0x4507, "ocp_thr"}, /* ocp_thr threshold level for OCP , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitchfilter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45c0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* BypassOCPCounter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypasslost_clk detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0x4600, "bypass_gc"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* gain control by means of MTP or i2c , */\ + { 0x4627, "cs_gain"}, /* + / - 128 steps in steps of 1/4 % 2's compliment , */\ + { 0x46a0, "bypass_lp"}, /* bypass Low-Pass filter in temperature sensor , */\ + { 0x46b0, "bypass_pwmcounter"}, /* bypass_pwmcounter , */\ + { 0x46c0, "ctrl_cs_negfixed"}, /* does not switch to neg , */\ + { 0x46d2, "ctrl_cs_neghyst"}, /* switches to neg depending on level , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x4800, "ctrl_negin"}, /* negin , */\ + { 0x4810, "ctrl_cs_sein"}, /* cs_sein , */\ + { 0x4820, "ctrl_coincidencecs"}, /* Coincidence current sense , */\ + { 0x4830, "ctrl_iddqtestbst"}, /* for iddq testing in powerstage of boost convertor , */\ + { 0x4840, "ctrl_coincidencebst"}, /* Switch protection on to prevent simultaniously switching power stages bst and amp, */\ + { 0x4851, "clock_sh_sel"}, /* Clock SH selection , */\ + { 0x4876, "delay_se_neg"}, /* delay of se and neg , */\ + { 0x48e1, "ctrl_cs_ttrack"}, /* sample & hold track time , */\ + { 0x4900, "ctrl_bypassclip"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4910, "ctrl_bypassclip2"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4920, "ctrl_clkgateCFoff"}, /* to disable clock gating in the coolflux , */\ + { 0x4930, "ctrl_testabst"}, /* testabst , */\ + { 0x4940, "ctrl_clipfast"}, /* clock switch for battery protection clipper, it switches back to old frequency, */\ + { 0x4950, "ctrl_cs_8ohm"}, /* 8 ohm mode for current sense (gain mode) , */\ + { 0x4960, "reserved"}, /* reserved , */\ + { 0x4974, "delay_clock_sh"}, /* delay_sh, tunes S7H delay , */\ + { 0x49c0, "inv_clksh"}, /* Invert the sample/hold clock for current sense ADC, */\ + { 0x49d0, "inv_neg"}, /* Invert neg signal , */\ + { 0x49e0, "inv_se"}, /* Invert se signal , */\ + { 0x49f0, "setse"}, /* switches between Single Ende and differentail mode, */\ + { 0x4a12, "ctrl_adc10_sel"}, /* select the input to convert the 10b ADC , */\ + { 0x4a60, "ctrl_adc10_reset"}, /* Global asynchronous reset (active HIGH) 10 bit ADC, */\ + { 0x4a81, "ctrl_adc10_test"}, /* Test mode selection signal 10 bit ADC , */\ + { 0x4aa0, "ctrl_bypass_lp_vbat"}, /* lp filter in batt sensor , */\ + { 0x4ae0, "ctrl_dc_offset"}, /* switch offset control on/off, is decimator offset control, */\ + { 0x4af0, "ctrl_tsense_hibias"}, /* bit to set the biasing in temp sensor to high , */\ + { 0x4b00, "ctrl_adc13_iset"}, /* Micadc Setting of current consumption. Debug use only, */\ + { 0x4b14, "ctrl_adc13_gain"}, /* Micadc gain setting (2-compl) , */\ + { 0x4b61, "ctrl_adc13_slowdel"}, /* Micadc Delay setting for internal clock. Debug use only, */\ + { 0x4b83, "ctrl_adc13_offset"}, /* Micadc ADC offset setting , */\ + { 0x4bc0, "ctrl_adc13_bsoinv"}, /* Micadc bit stream output invert mode for test , */\ + { 0x4bd0, "ctrl_adc13_resonator_enable"}, /* Micadc Give extra SNR with less stability. Debug use only, */\ + { 0x4be0, "ctrl_testmicadc"}, /* Mux at input of MICADC for test purpose , */\ + { 0x4c0f, "ctrl_offset"}, /* offset control for ABIST testing , */\ + { 0x4d05, "ctrl_windac"}, /* for testing direct control windac , */\ + { 0x4d65, "ctrl_peakcur"}, /* Control peakcur , */\ + { 0x4dc3, "pwm_dcc_cnt"}, /* control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0x4e04, "ctrl_slopecur"}, /* for testing direct control slopecur , */\ + { 0x4e53, "ctrl_dem"}, /* dyn element matching control, rest of codes are optional, */\ + { 0x4e93, "ctrl_demmismatch"}, /* dyn element matching add offset , */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* to enable direct control of pwm duty cycle , */\ + { 0x5007, "gain"}, /* gain setting of the gain multiplier gain need to increase with factor 1.41 (3dB), */\ + { 0x5081, "ctrl_sourceb"}, /* Set OUTB to , */\ + { 0x50a1, "ctrl_sourcea"}, /* Set OUTA to , */\ + { 0x50c1, "ctrl_sourcebst"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0x50e1, "ctrl_test_mono"}, /* ABIST mode to add both amplifier halfs as stereo or one amplifier half as mono, */\ + { 0x5104, "pulselengthbst"}, /* pulselength setting test input for boost converter , */\ + { 0x5150, "ctrl_bypasslatchbst"}, /* bypass_latch in boost converter , */\ + { 0x5160, "invertbst"}, /* invert pwmbst test signal , */\ + { 0x5174, "pulselength"}, /* pulselength setting test input for amplifier , */\ + { 0x51c0, "ctrl_bypasslatch"}, /* bypass_latch in boost convert , */\ + { 0x51d0, "invertb"}, /* invert pwmb test signal , */\ + { 0x51e0, "inverta"}, /* invert pwma test signal , */\ + { 0x51f0, "ctrl_bypass_ctrlloop"}, /* bypass_ctrlloop bypasses the control loop of the amplifier, */\ + { 0x5200, "ctrl_test_discrete"}, /* tbd for rdson testing , */\ + { 0x5210, "ctrl_test_rdsona"}, /* tbd for rdson testing , */\ + { 0x5220, "ctrl_test_rdsonb"}, /* tbd for rdson testing , */\ + { 0x5230, "ctrl_test_rdsonbst"}, /* tbd for rdson testing , */\ + { 0x5240, "ctrl_test_cvia"}, /* tbd for rdson testing , */\ + { 0x5250, "ctrl_test_cvib"}, /* tbd for rdson testing , */\ + { 0x5260, "ctrl_test_cvibst"}, /* tbd for rdson testing , */\ + { 0x5290, "test_bypass_pwmdiscretea"}, /* for testing ( ABIST) , */\ + { 0x52a0, "test_bypass_pwmdiscreteb"}, /* for testing ( ABIST) , */\ + { 0x52b0, "ctrl_clipc_forcehigh"}, /* test signal for clipcontrol , */\ + { 0x52c0, "ctrl_clipc_forcelow"}, /* test signal for clipcontrol , */\ + { 0x52d0, "ctrl_test_sdelta"}, /* for testing ( ABIST) , */\ + { 0x52e0, "ctrl_test_swhvp"}, /* for testing ( ABIST) , */\ + { 0x52f0, "test_gain_reduction"}, /* test gain reduction , */\ + { 0x5303, "ctrl_digimux_out_test1"}, /* Digimux TEST1 out , */\ + { 0x5343, "ctrl_digimux_out_test2"}, /* Digimux TEST2 out. output flag_clipa_low depending on cntr_bypassclip setting, */\ + { 0x5383, "ctrl_digimux_out_data1"}, /* Digimux DATA1 out (output flag_clipb_high depending on cntr_bypassclip setting), */\ + { 0x53c3, "ctrl_digimux_out_data3"}, /* Digimux DATA3 out (output flag_clipx_x depending on cntr_bypassclip setting), */\ + { 0x5400, "hs_mode"}, /* hs_mode, high speed mode I2C bus , */\ + { 0x5412, "test_parametric_io"}, /* test_parametric_io for testing pads , */\ + { 0x5440, "enbl_ringo"}, /* enbl_ringo, for test purpose to check with ringo , */\ + { 0x5480, "ctrl_cliplevel"}, /* Clip level , */\ + { 0x5491, "ctrl_anamux_sel"}, /* anamux selection , */\ + { 0x54b0, "test_vdddsw_dio"}, /* to overrule the power switches for memory , */\ + { 0x54c0, "ctrl_bypass_diosw_ovp"}, /* To disable the overvoltage protection of vddd_dio_sw, */\ + { 0x54d0, "test_vddd_sw"}, /* test vdd sw , */\ + { 0x54e0, "test_vddd_sw_comp"}, /* test vdd sw comp , */\ + { 0x550e, "enbl_amp"}, /* enbl_amp for testing to enable all analoge blocks in amplifier, */\ + { 0x55f0, "fr_fsp"}, /* extr free running clock mode for testing , */\ + { 0x5600, "use_direct_ctrls"}, /* use_direct_ctrls, to overrule several functions direct for testing, */\ + { 0x5610, "rst_datapath"}, /* rst_datapath, datapath reset , */\ + { 0x5620, "rst_cgu"}, /* rst_cgu, cgu reset , */\ + { 0x5637, "enbl_ref"}, /* for testing to enable all analoge blocks in references, */\ + { 0x56b0, "enbl_engage"}, /* Enable output stage amplifier , */\ + { 0x56c0, "use_direct_clk_ctrl"}, /* use_direct_clk_ctrl, to overrule several functions direct for testing, */\ + { 0x56d0, "use_direct_pll_ctrl"}, /* use_direct_pll_ctrl, to overrule several functions direct for test, */\ + { 0x56e0, "use_direct_ctrls_2"}, /* use_direct_sourseamp_ctrls, to overrule several functions direct for testing, */\ + { 0x5707, "ctrl_anamux_out_test1"}, /* Anamux control , */\ + { 0x5782, "ctrl_zero"}, /* Bandwith control feedbackloop , */\ + { 0x57b0, "enbl_ldo_stress"}, /* LDO stress function frinch capacitors , */\ + { 0x57c0, "ctrl_ocptest"}, /* ctrl_ocptest, deactivates the over current protection in the power stages of the amplifier. The ocp flag signals stay active., */\ + { 0x57e0, "ctrl_otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "ctrl_reverse"}, /* CTRL revers , */\ + { 0x5802, "pll_mdec_msb"}, /* most significant bits pll_mdec , */\ + { 0x5833, "pll_selr"}, /* pll_selr , */\ + { 0x5874, "pll_selp"}, /* pll_selp , */\ + { 0x58c3, "pll_seli"}, /* pll_seli , */\ + { 0x5900, "pll_psel"}, /* pll_psel , */\ + { 0x5910, "use_direct_pll_psel"}, /* use_direct_pll_psel , */\ + { 0x5923, "nbck"}, /* NBCK , */\ + { 0x5960, "auto_nbck"}, /* AUTO_NBCK , */\ + { 0x5970, "pll_frm"}, /* pll_frm , */\ + { 0x5980, "pll_directi"}, /* pll_directi , */\ + { 0x5990, "pll_directo"}, /* pll_directo , */\ + { 0x59a0, "enbl_PLL"}, /* enbl_PLL , */\ + { 0x59b0, "sel_clkout"}, /* SEL_CLKOUT , */\ + { 0x59e0, "fr_lost_clk"}, /* fr_lost_clk , */\ + { 0x59f0, "pll_bypass"}, /* pll_bypass , */\ + { 0x5a0f, "tsig_freq"}, /* tsig_freq, internal sinus test generator, frequency control, */\ + { 0x5b02, "tsig_freq_msb"}, /* select internal sinus test generator, frequency control msb bits, */\ + { 0x5b30, "inject_tsig"}, /* inject_tsig, control bit to switch to internal sinus test generator, */\ + { 0x5b44, "ctrl_adc10_prog_sample"}, /* control ADC10 , */\ + { 0x5c01, "pll_ndec_msb"}, /* most significant bits of pll_ndec , */\ + { 0x5c2d, "pll_mdec"}, /* bits 13..0 of pll_mdec , */\ + { 0x5d06, "pll_pdec"}, /* pll_pdec , */\ + { 0x5d87, "pll_ndec"}, /* bits 7..0 of pll_ndec , */\ + { 0x5e00, "pdm_ch_sel_reg"}, /* PDM channel selection , */\ + { 0x5e10, "pdm_iis_rst_reg"}, /* PDM Interface reset , */\ + { 0x5e20, "clk_src_sel_reg"}, /* WS Source Selection , */\ + { 0x5e70, "pdm_resync_bypass"}, /* PDM resynchronization bypass , */\ + { 0x6007, "MTP_key1"}, /* MTP Key1 , */\ + { 0x6185, "mtp_ecc_tcin"}, /* Mtp_ecc_tcin , */\ + { 0x6203, "mtp_man_address_in"}, /* address from i2cregs for writing one word single mtp, */\ + { 0x6260, "mtp_ecc_eeb"}, /* enable code bit generation (active low!) , */\ + { 0x6270, "mtp_ecc_ecb"}, /* enable correction signal (active low!) , */\ + { 0x6280, "man_copy_mtp_to_iic"}, /* start copying single word from mtp to i2cregs_mtp , */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* start copying single word from i2cregs_mtp to mtp [Key 1 protected], */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* start copying all the data from mtp to i2cregs_mtp, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x62d2, "mtp_speed_mode"}, /* Speed mode , */\ + { 0x6340, "mtp_dircet_enable"}, /* mtp_direct_enable (key1 protected) , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr (key1 protected) direct value for mtp pin wr. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd (key1 protected) direct value for mtp pin rd. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst (key1 protected) direct value for mtp pin rst. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers (key1 protected) direct value for mtp pin ers. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg (key1 protected) direct value for mtp pin prg. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp (key1 protected) direct value for mtp pin epp. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test (key1 protected) , */\ + { 0x640f, "mtp_man_data_in"}, /* single wordt be written to MTP (manual copy) , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "cf_int"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "cf_req"}, /* request for access (8 channels) , */\ + { 0x710f, "cf_madd"}, /* memory-address to be accessed , */\ + { 0x720f, "cf_mema"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "cf_err"}, /* cf error Flags , */\ + { 0x7387, "cf_ack"}, /* acknowledge of requests (8 channels")" , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "calibr_ron_done"}, /* (key2 protected) calibration of Ron has been executed, */\ + { 0x8105, "calibr_vout_offset"}, /* calibr_vout_offset (DCDCoffset) 2's compliment (key1 protected), */\ + { 0x8163, "calibr_delta_gain"}, /* delta gain for vamp (alpha) 2's compliment (key1 protected), */\ + { 0x81a5, "calibr_offs_amp"}, /* offset for vamp (Ampoffset) 2's compliment (key1 protected), */\ + { 0x8207, "calibr_gain_cs"}, /* gain current sense (Imeasalpha) 2's compliment (key1 protected), */\ + { 0x8284, "calibr_temp_offset"}, /* temperature offset 2's compliment (key1 protected), */\ + { 0x82d2, "calibr_temp_gain"}, /* temperature gain 2's compliment (key1 protected) , */\ + { 0x830f, "calibr_ron"}, /* Ron resistance of coil (key1 protected) , */\ + { 0x8406, "ctrl_offset_a"}, /* Offset of amplifier level shifter , */\ + { 0x8486, "ctrl_offset_b"}, /* Offset of amplifier level shifter , */\ + { 0x850f, "type_bits_HW"}, /* HW Bits , */\ + { 0x860f, "type_bits1_SW"}, /* MTP-control SW1 , */\ + { 0x870f, "type_bits2_SW"}, /* MTP-control SW2 , */\ + { 0x8a0f, "production_data1"}, /* (key1 protected) , */\ + { 0x8b0f, "production_data2"}, /* (key1 protected) , */\ + { 0x8c0f, "production_data3"}, /* (key1 protected) , */\ + { 0x8d0f, "production_data4"}, /* (key1 protected) , */\ + { 0x8e0f, "production_data5"}, /* (key1 protected) , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + + +#endif /* TFA_INC_TFA9891_TFAFIELDNAMES_H_ */ diff --git a/sound/soc/codecs/tfa9891/tfa9897_init.c b/sound/soc/codecs/tfa9891/tfa9897_init.c new file mode 100755 index 000000000000..3d5e78d0afc9 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa9897_init.c @@ -0,0 +1,129 @@ +/* + *Copyright 2014,2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "tfa_internal.h" + +#include "tfa98xx_tfafieldnames.h" + +static enum Tfa98xx_Error tfa9897_specific(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short check_value; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers must already set to default POR value */ + + /* $48:[3] - 1 ==> 0; iddqtestbst - default value changed. + * When Iddqtestbst is set to "0", the slewrate is reduced. + * This will lower the overshoot on IN-B to avoid NMOS damage of booster. + */ + error = tfa98xx_write_register16(handle, 0x48, 0x0300); /* POR value = 0x308 */ + + /* $49:[0] - 1 ==> 0; CLIP - default value changed. 0 means CLIPPER on + */ + error = tfa98xx_read_register16(handle, 0x49, &check_value); + check_value &= ~0x1; + error = tfa98xx_write_register16(handle, 0x49, check_value); + + return error; +} + +/* + * the int24 values for the vsfw delay table + */ +static unsigned char vsfwdelay_table[] = { + 0,0,2, /*Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0,0, /*Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0,0, /*Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,2, /*Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,2, /*Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,2, /*Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,2, /*Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /*Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,3 /*Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +/* + * TODO make this tfa98xx + * Note that the former products write this table via the patch + * so moving this to the tfa98xx API requires also updating all patches + */ +static enum Tfa98xx_Error tfa9897_dsp_write_vsfwdelay_table(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error; + error = tfa_dsp_cmd_id_write(handle, MODULE_FRAMEWORK, + TFA1_FW_PAR_ID_SET_CURRENT_DELAY, + sizeof(vsfwdelay_table), + vsfwdelay_table); + return error; +} + +/* + * The int24 values for the fracdelay table + * For now applicable only for 8 and 48 kHz + */ +static unsigned char cvfracdelay_table[] ={ + 0,0,51, /*Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0, 0, /*Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0, 0, /*Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,38, /*Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,34, /*Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,33, /*Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,11, /*Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /*Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,62 /*Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +enum Tfa98xx_Error tfa9897_dsp_write_cvfracdelay_table(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error; + error = tfa_dsp_cmd_id_write(handle, MODULE_FRAMEWORK, + TFA1_FW_PAR_ID_SET_CURFRAC_DELAY, + sizeof(cvfracdelay_table), + cvfracdelay_table); + return error; +} + +static enum Tfa98xx_Error tfa9897_tfa_dsp_write_tables(Tfa98xx_handle_t dev_idx, int sample_rate) +{ + enum Tfa98xx_Error error; + + /* Not used for max1! */ + sample_rate=sample_rate; + + error = tfa9897_dsp_write_vsfwdelay_table(dev_idx); + if (error == Tfa98xx_Error_Ok) { + error = tfa9897_dsp_write_cvfracdelay_table(dev_idx); + } + + tfa98xx_dsp_reset(dev_idx, 1); + tfa98xx_dsp_reset(dev_idx, 0); + + return error; +} + +/* + * register device specifics functions + */ +void tfa9897_ops(struct tfa_device_ops *ops) { + ops->tfa_init=tfa9897_specific; + ops->tfa_dsp_write_tables=tfa9897_tfa_dsp_write_tables; +} diff --git a/sound/soc/codecs/tfa9891/tfa98xx.c b/sound/soc/codecs/tfa9891/tfa98xx.c new file mode 100644 index 000000000000..0f45fecbab01 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa98xx.c @@ -0,0 +1,3637 @@ +/* + * tfa98xx.c tfa98xx codec module + * + * Copyright (c) 2015 NXP Semiconductors + * + * Author: Sebastien Jan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) "%s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for l21 power*/ +#include + + +#define I2C_RETRIES 50 +#define I2C_RETRY_DELAY 5 /* ms */ +/* TODO: remove genregs usage? */ +#ifdef N1A +#include "tfa98xx_genregs_N1A12.h" +#else +#include "tfa98xx_genregs_N1C.h" +#endif +#include "tfa9891_genregs.h" + +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" +#include "tfa.h" +#include "tfa_service.h" +#include "tfa_container.h" +#include "tfa98xx_parameters.h" + +#define TFA98XX_VERSION "2.10.2" + +/* Change volume selection behavior: + * Uncomment following line to generate a profile change when updating + * a volume control (also changes to the profile of the modified volume + * control) + */ +/*#define TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL 1 +*/ + +/* Supported rates and data formats */ +#define TFA98XX_RATES SNDRV_PCM_RATE_8000_48000 + +//#define TFA98XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +#define TFA98XX_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +#define TF98XX_MAX_DSP_START_TRY_COUNT 10 + +#define XMEM_TAP_ACK 0x0122 +#define XMEM_TAP_READ 0x010f + +static LIST_HEAD(profile_list); /* list of user selectable profiles */ + +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif + +#ifdef dev_info +#undef dev_info +#define dev_info dev_err +#endif + +static int tfa98xx_kmsg_regs = 0; +static int tfa98xx_ftrace_regs = 0; + +static struct tfa98xx *tfa98xx_devices[4] = {NULL, NULL, NULL, NULL}; +static int tfa98xx_registered_handles = 0; +static int tfa98xx_vsteps[4]={0,0,0,0}; +static int tfa98xx_profile = 0; /* store profile */ +static int tfa98xx_prof_vsteps[10] = {0}; /* store vstep per profile (single device) */ +static int tfa98xx_mixer_profiles = 0; /* number of user selectable profiles */ +static int tfa98xx_mixer_profile = 0; /* current mixer profile */ + +static char *dflt_prof_name = ""; +module_param(dflt_prof_name, charp, S_IRUGO); + +static int no_start = 0; +module_param(no_start, int, S_IRUGO); +MODULE_PARM_DESC(no_start, "do not start the work queue; for debugging via user\n"); + +struct tfa98xx *g_tfa98xx = NULL; +EXPORT_SYMBOL_GPL(g_tfa98xx); + +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for l21 power*/ +struct regulator *bob_power; +EXPORT_SYMBOL_GPL(bob_power); + +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx); +static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx); +static int tfa98xx_get_fssel(unsigned int rate); + +static int get_profile_from_list(char *buf, int id); +static int get_profile_id_for_sr(int id, unsigned int rate); +/*zhiguang.su@MultiMediaService,2017-02-09,avoid no sound for ftm*/ +static void tfa98xx_dsp_startInit(struct tfa98xx *tfa98xx); +/*zhiguang.su@MultiMediaService,2017-04-26,add ftm spk pa rivision test*/ +static int tfa98xx_info_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +static int tfa98xx_get_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int tfa98xx_set_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +/*zhiguang.su@MultiMedia.AudioDrv, 2015-11-09, add for debug*/ +int testLogOn = 0; +EXPORT_SYMBOL_GPL(testLogOn); + +/*wangdongdong@MultiMediaService,2016/11/30,add for speaker impedence detection*/ +static int tfa98xx_speaker_recalibration(Tfa98xx_handle_t handle,unsigned int *speakerImpedance); + +struct tfa98xx_rate { + unsigned int rate; + unsigned int fssel; +}; + +static struct tfa98xx_rate rate_to_fssel[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, +}; + +/*zhiguang.su@MultiMediaService,2017-04-26,add ftm spk pa rivision test*/ +static struct snd_kcontrol_new tfa98xx_at_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "SPK_PA rivision", + .info = tfa98xx_info_rivision_ctl, + .get = tfa98xx_get_rivision_ctl, + .put = tfa98xx_set_rivision_ctl, + }, +}; +static ssize_t tfa98xx_state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_err("%s",__func__); + + return 0; +} +static ssize_t tfa98xx_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct snd_soc_codec *codec; + struct tfa98xx *tfa98xx; + char *str; + uint16_t status; + int ret, calibrate_done; +/*wangdongdong@MultiMediaService,2016/11/30,add for speaker impedence detection*/ + unsigned int speakerImpedance1 = 0; + if(g_tfa98xx == NULL) + { + pr_err("%s g_tfa98xx = NULL\n",__func__); + return 0; + } + + tfa98xx = g_tfa98xx; + codec = tfa98xx->codec; + + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_open(tfa98xx->handle); + if (ret) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + + /* Need to ensure DSP is access-able, use mtp read access for this + * purpose + */ + ret = tfa98xx_get_mtp(tfa98xx->handle, &status); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + ret = tfaRunWaitCalibration(tfa98xx->handle, &calibrate_done); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto r_c_err; + } + + switch (calibrate_done) { + case 1: + /* calibration complete ! */ + tfa98xx_speaker_recalibration(tfa98xx->handle,&speakerImpedance1); + pr_err("tfa speaker calibration impedance = %d\n",speakerImpedance1); + ret = print_calibration_modify(tfa98xx->handle, str, PAGE_SIZE); + + break; + case 0: + case -1: + ret = scnprintf(str, PAGE_SIZE, "%d\n", calibrate_done); + break; + default: + pr_err("Unknown calibration status: %d\n", calibrate_done); + ret = -EINVAL; + } + pr_err("calib_done: %d - ret = %d - %s", calibrate_done, ret, str); + + if (ret < 0) + goto r_err; + //modify for EngineerMode detection, different from 15801 + if(calibrate_done == 1) + calibrate_done = 2; + //ret = simple_read_from_buffer(buf, count, ppos, str, ret); + ret = sprintf(buf,"%d:%d",calibrate_done,speakerImpedance1); + +r_err: + kfree(str); +r_c_err: + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + return ret; +} + +static struct device_attribute tfa98xx_state_attr = + __ATTR(calibra, 0444, tfa98xx_state_show, tfa98xx_state_store); + +/*zhiguang.su@MultiMedia.AudioDrv, 2015-11-05, add for debug*/ +static ssize_t tfa98xx_Log_state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_err("%s",__func__); + + if (sysfs_streq(buf, "LogOn")) + { + testLogOn = 1; + } + else if(sysfs_streq(buf, "LogOff")) + { + testLogOn = 0; + } + else + { + testLogOn = 0; + count = -EINVAL; + } + return count; +} + +static ssize_t tfa98xx_Log_state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return 0; +} + +static struct device_attribute tfa98xx_Log_state_attr = + __ATTR(Log, S_IWUSR|S_IRUGO, tfa98xx_Log_state_show, tfa98xx_Log_state_store); + + +/* Wrapper for tfa start */ +static enum tfa_error tfa98xx_tfa_start(struct tfa98xx *tfa98xx, int next_profile, int *vstep) +{ + enum tfa_error err; + + err = tfa_start(next_profile, vstep); + + /* Check and update tap-detection state (in case of profile change) */ + tfa98xx_tapdet_check_update(tfa98xx); + + /* A cold start erases the configuration, including interrupts setting. + * Restore it if required + */ + tfa98xx_interrupt_restore(tfa98xx); + + return err; +} + +static int tfa98xx_input_open(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + dev_dbg(tfa98xx->codec->dev, "opening device file\n"); + + /* note: open function is called only once by the framework. + * No need to count number of open file instances. + */ + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + dev_dbg(&tfa98xx->i2c->dev, + "DSP not loaded, cannot start tap-detection\n"); + return -EIO; + } + + /* enable tap-detection service */ + tfa98xx->tapdet_open = true; + tfa98xx_tapdet_check_update(tfa98xx); + + return 0; +} + +static void tfa98xx_input_close(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + + dev_dbg(tfa98xx->codec->dev, "closing device file\n"); + + /* Note: close function is called if the device is unregistered */ + + /* disable tap-detection service */ + tfa98xx->tapdet_open = false; + tfa98xx_tapdet_check_update(tfa98xx); +} + +static int tfa98xx_register_inputdev(struct tfa98xx *tfa98xx) +{ + int err; + struct input_dev *input; + input = input_allocate_device(); + + if (!input) { + dev_err(tfa98xx->codec->dev, "Unable to allocate input device\n"); + return -ENOMEM; + } + + input->evbit[0] = BIT_MASK(EV_KEY); + input->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + input->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + input->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + input->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + input->keybit[BIT_WORD(BTN_4)] |= BIT_MASK(BTN_4); + input->keybit[BIT_WORD(BTN_5)] |= BIT_MASK(BTN_5); + input->keybit[BIT_WORD(BTN_6)] |= BIT_MASK(BTN_6); + input->keybit[BIT_WORD(BTN_7)] |= BIT_MASK(BTN_7); + input->keybit[BIT_WORD(BTN_8)] |= BIT_MASK(BTN_8); + input->keybit[BIT_WORD(BTN_9)] |= BIT_MASK(BTN_9); + + input->open = tfa98xx_input_open; + input->close = tfa98xx_input_close; + + input->name = "tfa98xx-tapdetect"; + + input->id.bustype = BUS_I2C; + input_set_drvdata(input, tfa98xx); + + err = input_register_device(input); + if (err) { + dev_err(tfa98xx->codec->dev, "Unable to register input device\n"); + goto err_free_dev; + } + + dev_dbg(tfa98xx->codec->dev, "Input device for tap-detection registered: %s\n", + input->name); + tfa98xx->input = input; + return 0; + +err_free_dev: + input_free_device(input); + return err; +} + +/* + * Check if an input device for tap-detection can and shall be registered. + * Register it if appropriate. + * If already registered, check if still relevant and remove it if necessary. + * unregister: true to request inputdev unregistration. + */ +static void __tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx, bool unregister) +{ + bool tap_profile = false; + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strstr(tfaContProfileName(tfa98xx->handle, i), ".tap")) { + tap_profile = true; + tfa98xx->tapdet_profiles |= 1 << i; + dev_info(tfa98xx->codec->dev, + "found a tap-detection profile (%d - %s)\n", + i, tfaContProfileName(tfa98xx->handle, i)); + } + } + + /* Check for device support: + * - at device level + * - at container (profile) level + */ + if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) || + !tap_profile || + unregister) { + /* No input device supported or required */ + if (tfa98xx->input) { + input_unregister_device(tfa98xx->input); + tfa98xx->input = NULL; + } + return; + } + + /* input device required */ + if (tfa98xx->input) + dev_info(tfa98xx->codec->dev, "Input device already registered, skipping\n"); + else + tfa98xx_register_inputdev(tfa98xx); +} + +static void tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, false); +} + +static void tfa98xx_inputdev_unregister(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, true); +} + +#ifdef CONFIG_DEBUG_FS +/* OTC reporting + * Returns the MTP0 OTC bit value + */ +static int tfa98xx_dbgfs_otc_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc); + enum Tfa98xx_Error err, status; + unsigned short value; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + + err = tfa98xx_get_mtp(tfa98xx->handle, &value); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (otc->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + if (otc->rd_valid) { + /* read cached value */ + *val = otc->rd_value; + pr_err("Returning cached value of OTC: %llu\n", *val); + } else { + pr_info("OTC value never read!\n"); + return -EIO; + } + return 0; + } + } + + *val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS; + pr_err("OTC : %d\n", value&1); + + if (otc->deferrable) { + otc->rd_value = *val; + otc->rd_valid = true; + } + + return 0; +} + +static int tfa98xx_dbgfs_otc_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc); + enum Tfa98xx_Error err, status; + + if (val != 0 && val != 1) { + pr_err("Unexpected value %llu\n\n", val); + return -EINVAL; + } + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_set_mtp(tfa98xx->handle, + (val << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS) + & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK, + TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (otc->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + /* defer OTC */ + otc->wr_value = val; + otc->triggered = true; + pr_err("Deferring write to OTC (%d)\n", otc->wr_value); + return 0; + } + } + + /* deferrable: cache the value for subsequent offline read */ + if (otc->deferrable) { + otc->rd_value = val; + otc->rd_valid = true; + } + + pr_err("otc < %llu\n", val); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error err, status; + unsigned short value; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_get_mtp(tfa98xx->handle, &value); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != Tfa98xx_Error_Ok) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } + + *val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS; + pr_err("MTPEX : %d\n", value & 2 >> 1); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *mtpex = &(handles_local[tfa98xx->handle].dev_ops.controls.mtpex); + enum Tfa98xx_Error err, status; + + if (val != 0) { + pr_err("Can only clear MTPEX (0 value expected)\n"); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_set_mtp(tfa98xx->handle, 0, + TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (mtpex->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + /* defer OTC */ + mtpex->wr_value = 0; + mtpex->triggered = true; + pr_err("Deferring write to MTPEX (%d)\n", mtpex->wr_value); + return 0; + } + } + + pr_err("mtpex < 0\n"); + + return 0; +} + +static int tfa98xx_dbgfs_temp_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error status; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + *val = tfa98xx_get_exttemp(tfa98xx->handle); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + return 0; +} + +static int tfa98xx_dbgfs_temp_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error status; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + tfa98xx_set_exttemp(tfa98xx->handle, (short)val); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + return 0; +} + +/* + * calibration: + * write key phrase to the 'calibration' file to trigger a new calibration + * read the calibration file once to get the calibration result + */ +/* tfa98xx_deferred_calibration_status - called from tfaRunWaitCalibration */ +void tfa98xx_deferred_calibration_status(Tfa98xx_handle_t handle, int calibrateDone) +{ + struct tfa98xx *tfa98xx = tfa98xx_devices[handle]; + struct tfa98xx_control *calib = &(handles_local[handle].dev_ops.controls.calib); + + if (calib->wr_value) { + /* a calibration was programmed from the calibration file + * interface + */ + switch (calibrateDone) { + case 1: + /* calibration complete ! */ + calib->wr_value = false; /* calibration over */ + calib->rd_valid = true; /* result available */ + calib->rd_value = true; /* result valid */ + tfa_dsp_get_calibration_impedance(tfa98xx->handle); + wake_up_interruptible(&tfa98xx->wq); + break; + case 0: + pr_info("Calibration not complete, still waiting...\n"); + break; + case -1: + pr_info("Calibration failed\n"); + calib->wr_value = false; /* calibration over */ + calib->rd_valid = true; /* result available */ + calib->rd_value = false; /* result not valid */ + wake_up_interruptible(&tfa98xx->wq); + break; + default: + pr_info("Unknown calibration status: %d\n", + calibrateDone); + } + } +} + +static ssize_t tfa98xx_dbgfs_start_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib); + char *str; + int ret; + + ret = wait_event_interruptible(tfa98xx->wq, calib->wr_value == false); + + if (ret == -ERESTARTSYS) { + /* interrupted by signal */ + return ret; + } + + if (!calib->rd_valid) + /* no calibration result available - skip */ + return 0; + + if (calib->rd_value) { + /* Calibration already complete, return result */ + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) + return -ENOMEM; + ret = print_calibration(tfa98xx->handle, str, PAGE_SIZE); + if (ret < 0) { + kfree(str); + return ret; + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + + pr_err("%s", str); + kfree(str); + calib->rd_value = false; + } else { + /* Calibration failed, return the error code */ + const char estr[] = "-1\n"; + ret = copy_to_user(user_buf, estr, sizeof(estr)); + if (ret) + return -EFAULT; + ret = sizeof(estr); + } + calib->rd_valid = false; + return ret; +} + +static ssize_t tfa98xx_dbgfs_start_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib); + enum Tfa98xx_Error ret; + char buf[32]; + const char ref[] = "please calibrate now"; + int buf_size; + + /* check string length, and account for eol */ + if (count > sizeof(ref) + 1 || count < (sizeof(ref) - 1)) + return -EINVAL; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare string, excluding the trailing \0 and the potentials eol */ + if (strncmp(buf, ref, sizeof(ref) - 1)) + return -EINVAL; + + /* Do not open/close tfa98xx: not required by tfa_clibrate */ + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_calibrate(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if(ret) { + pr_info("Calibration start failed (%d), deferring...\n", ret); + calib->triggered = true; + } else { + pr_info("Calibration started\n"); + } + calib->wr_value = true; /* request was triggered from here */ + calib->rd_valid = false; /* result not available */ + calib->rd_value = false; /* result not valid (dafault) */ + + return count; +} + +static ssize_t tfa98xx_dbgfs_r_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char *str; + uint16_t status; + int ret, calibrate_done; +/*wangdongdong@MultiMediaService,2016/11/30,add for speaker impedence detection*/ + unsigned int speakerImpedance1 = 0; + + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_open(tfa98xx->handle); + if (ret) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + + /* Need to ensure DSP is access-able, use mtp read access for this + * purpose + */ + ret = tfa98xx_get_mtp(tfa98xx->handle, &status); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + ret = tfaRunWaitCalibration(tfa98xx->handle, &calibrate_done); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto r_c_err; + } + + switch (calibrate_done) { + case 1: + /* calibration complete ! */ + tfa98xx_speaker_recalibration(tfa98xx->handle,&speakerImpedance1); + pr_err("tfa speaker calibration impedance = %d\n",speakerImpedance1); + ret = print_calibration_modify(tfa98xx->handle, str, PAGE_SIZE); + + break; + case 0: + case -1: + ret = scnprintf(str, PAGE_SIZE, "%d\n", calibrate_done); + break; + default: + pr_err("Unknown calibration status: %d\n", calibrate_done); + ret = -EINVAL; + } + pr_err("calib_done: %d - ret = %d - %s", calibrate_done, ret, str); + + if (ret < 0) + goto r_err; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + +r_err: + kfree(str); +r_c_err: + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + return ret; +} + +static ssize_t tfa98xx_dbgfs_version_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + char str[] = TFA98XX_VERSION "\n"; + int ret; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, sizeof(str)); + + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int ret = 0; + char *str; + + switch (tfa98xx->dsp_init) { + case TFA98XX_DSP_INIT_STOPPED: + str = "Stopped\n"; + break; + case TFA98XX_DSP_INIT_RECOVER: + str = "Recover requested\n"; + break; + case TFA98XX_DSP_INIT_FAIL: + str = "Failed init\n"; + break; + case TFA98XX_DSP_INIT_PENDING: + str = "Pending init\n"; + break; + case TFA98XX_DSP_INIT_DONE: + str = "Init complete\n"; + break; + default: + str = "Invalid\n"; + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error ret; + char buf[32]; + const char start_cmd[] = "start"; + const char stop_cmd[] = "stop"; + const char mon_start_cmd[] = "monitor start"; + const char mon_stop_cmd[] = "monitor stop"; + int buf_size; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare strings, excluding the trailing \0 */ + if (!strncmp(buf, start_cmd, sizeof(start_cmd) - 1)) { + pr_info("Manual triggering of dsp start...\n"); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + mutex_unlock(&tfa98xx->dsp_lock); + pr_err("tfa_start complete: %d\n", ret); + } else if (!strncmp(buf, stop_cmd, sizeof(stop_cmd) - 1)) { + pr_info("Manual triggering of dsp stop...\n"); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_stop(); + mutex_unlock(&tfa98xx->dsp_lock); + pr_err("tfa_stop complete: %d\n", ret); + } else if (!strncmp(buf, mon_start_cmd, sizeof(mon_start_cmd) - 1)) { + pr_info("Manual start of monitor thread...\n"); + } else if (!strncmp(buf, mon_stop_cmd, sizeof(mon_stop_cmd) - 1)) { + pr_info("Manual stop of monitor thread...\n"); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t tfa98xx_dbgfs_accounting_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char str[255]; + int ret; + int n = 0; + + n += snprintf(&str[n], sizeof(str)-1-n, "Wait4Src\t= %d\n", tfa98xx->count_wait_for_source_state); + n += snprintf(&str[n], sizeof(str)-1-n, "NOCLK\t\t= %d\n", tfa98xx->count_noclk); + + str[n+1] = '\0'; /* in case str is not large enough */ + + ret = simple_read_from_buffer(user_buf, count, ppos, str, n+1); + + return ret; +} + +static int tfa98xx_dbgfs_pga_gain_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int err; + unsigned int value; + +/* *val = TFA_GET_BF(tfa98xx->handle, SAAMGAIN);*/ + err = regmap_read(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA, &value); + *val = (value & TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK) >> + TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS; + return 0; +} + +static int tfa98xx_dbgfs_pga_gain_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int err; + unsigned int value; + + value = val & 0xffff; + if (value > 7) + return -EINVAL; +/* TFA_SET_BF(tfa98xx->handle, SAAMGAIN, value);*/ + err = regmap_update_bits(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA, + TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK, + value << TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS); + return err; +} + +/* Direct registers access - provide register address in hex */ +#define TFA98XX_DEBUGFS_REG_SET(__reg) \ +static int tfa98xx_dbgfs_reg_##__reg##_set(void *data, u64 val) \ +{ \ + struct i2c_client *i2c = (struct i2c_client *)data; \ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \ + unsigned int ret, value; \ + \ + ret = regmap_write(tfa98xx->regmap, 0x##__reg, (val & 0xffff)); \ + value = val & 0xffff; \ + return 0; \ +} \ +static int tfa98xx_dbgfs_reg_##__reg##_get(void *data, u64 *val) \ +{ \ + struct i2c_client *i2c = (struct i2c_client *)data; \ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \ + unsigned int value; \ + int ret; \ + ret = regmap_read(tfa98xx->regmap, 0x##__reg, &value); \ + *val = value; \ + return 0; \ +} \ +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_reg_##__reg##_fops, tfa98xx_dbgfs_reg_##__reg##_get, \ + tfa98xx_dbgfs_reg_##__reg##_set, "0x%llx\n"); + +#define VAL(str) #str +#define TOSTRING(str) VAL(str) +#define TFA98XX_DEBUGFS_REG_CREATE_FILE(__reg, __name) \ + debugfs_create_file(TOSTRING(__reg) "-" TOSTRING(__name), S_IRUGO|S_IWUGO, dbg_reg_dir,\ + i2c, &tfa98xx_dbgfs_reg_##__reg##_fops); + + +TFA98XX_DEBUGFS_REG_SET(00); +TFA98XX_DEBUGFS_REG_SET(01); +TFA98XX_DEBUGFS_REG_SET(02); +TFA98XX_DEBUGFS_REG_SET(03); +TFA98XX_DEBUGFS_REG_SET(04); +TFA98XX_DEBUGFS_REG_SET(05); +TFA98XX_DEBUGFS_REG_SET(06); +TFA98XX_DEBUGFS_REG_SET(07); +TFA98XX_DEBUGFS_REG_SET(08); +TFA98XX_DEBUGFS_REG_SET(09); +TFA98XX_DEBUGFS_REG_SET(0A); +TFA98XX_DEBUGFS_REG_SET(0B); +TFA98XX_DEBUGFS_REG_SET(0F); +TFA98XX_DEBUGFS_REG_SET(10); +TFA98XX_DEBUGFS_REG_SET(11); +TFA98XX_DEBUGFS_REG_SET(12); +TFA98XX_DEBUGFS_REG_SET(13); +TFA98XX_DEBUGFS_REG_SET(22); +TFA98XX_DEBUGFS_REG_SET(25); + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_otc_fops, tfa98xx_dbgfs_otc_get, + tfa98xx_dbgfs_otc_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_mtpex_fops, tfa98xx_dbgfs_mtpex_get, + tfa98xx_dbgfs_mtpex_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_temp_fops, tfa98xx_dbgfs_temp_get, + tfa98xx_dbgfs_temp_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_pga_gain_fops, tfa98xx_dbgfs_pga_gain_get, + tfa98xx_dbgfs_pga_gain_set, "%llu\n"); + +static const struct file_operations tfa98xx_dbgfs_calib_start_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_start_get, + .write = tfa98xx_dbgfs_start_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_r_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_r_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_version_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_version_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_dsp_state_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_dsp_state_get, + .write = tfa98xx_dbgfs_dsp_state_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_accounting_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_accounting_get, + .llseek = default_llseek, +}; + + +static void tfa98xx_debug_init(struct tfa98xx *tfa98xx, struct i2c_client *i2c) +{ + char name[50]; + struct dentry *dbg_reg_dir; + + scnprintf(name, MAX_CONTROL_NAME, "%s-%x", i2c->name, i2c->addr); + tfa98xx->dbg_dir = debugfs_create_dir(name, NULL); + debugfs_create_file("OTC", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_otc_fops); + debugfs_create_file("MTPEX", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_mtpex_fops); + debugfs_create_file("TEMP", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_temp_fops); + debugfs_create_file("calibrate", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_start_fops); + debugfs_create_file("R", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_r_fops); + debugfs_create_file("version", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_version_fops); + debugfs_create_file("dsp-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_dsp_state_fops); + debugfs_create_file("accounting", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_accounting_fops); + + /* Direct registers access */ + if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV) { + dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir); + TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS); + TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER); + TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT); + TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR); + TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST); + TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION); + TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0A, I2S_SEL_REG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0B, HIDDEN_MTP_KEY2); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0F, INTERRUPT_REG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(10, PDM_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(11, PDM_OUT_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(12, PDM_DS4_R); + TFA98XX_DEBUGFS_REG_CREATE_FILE(13, PDM_DS4_L); + TFA98XX_DEBUGFS_REG_CREATE_FILE(22, CTRL_SAAM_PGA); + TFA98XX_DEBUGFS_REG_CREATE_FILE(25, MISC_CTRL); + } + + if (tfa98xx->flags & TFA98XX_FLAG_TFA9897_FAM_DEV) { + dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir); + TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS); + TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER); + TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT); + TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR); + TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST); + TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION); + TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL); + } + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + dev_dbg(tfa98xx->dev, "Adding pga_gain debug interface\n"); + debugfs_create_file("pga_gain", S_IRUGO, tfa98xx->dbg_dir, + tfa98xx->i2c, + &tfa98xx_dbgfs_pga_gain_fops); + } +} + +static void tfa98xx_debug_remove(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->dbg_dir) + debugfs_remove_recursive(tfa98xx->dbg_dir); +} +#endif +/*wangdongdong@MultiMediaService,2016/11/30,add for speaker impedence detection*/ +static int tfa98xx_speaker_recalibration(Tfa98xx_handle_t handle,unsigned int *speakerImpedance) +{ + int err, error = Tfa98xx_Error_Ok; + // struct tfa98xx *tfa98xx = container_of(&handle, struct tfa98xx, handle); + + + + /* Do not open/close tfa98xx: not required by tfa_clibrate */ + error = tfa_calibrate(handle); + /* powerdown CF */ +// error = tfa98xx_powerdown(handle, 1 ); + msleep_interruptible(25); + error = tfaRunSpeakerBoost(handle, 1, 0); /* No force coldstart (with profile 0) */ + if(error) { + pr_err("Calibration failed (error = %d)\n", error); + *speakerImpedance = 0; + } else { + pr_err("Calibration sucessful! \n"); + *speakerImpedance = handles_local[handle].mohm[0]; + pr_err("Calibration (*speakerImpedance= %d)\n", *speakerImpedance); + if (TFA_GET_BF(handle, PWDN) != 0) { + err = tfa98xx_powerdown(handle, 0); //leave power off state + } + tfaRunUnmute(handle); /* unmute */ + } + + return error; +} + +static int tfa98xx_get_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + int vstep = tfa98xx_prof_vsteps[profile]; + ucontrol->value.integer.value[0] = + tfacont_get_max_vstep(tfa98xx->handle, profile) + - vstep - 1; + return 0; +} + +static int tfa98xx_set_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + int value = ucontrol->value.integer.value[0]; + int vstep = tfa98xx_prof_vsteps[profile]; + int vsteps = tfacont_get_max_vstep(tfa98xx->handle, profile); + int new_vstep, err = 0; + int ready = 0; + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + if (no_start != 0) + return 0; + + if (vstep == vsteps - value - 1) + return 0; + + new_vstep = vsteps - value - 1; + + if (new_vstep < 0) + new_vstep = 0; + + tfa98xx_prof_vsteps[profile] = new_vstep; + +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + if (profile == tfa98xx_profile) { +#endif + /* this is the active profile, program the new vstep */ + tfa98xx_vsteps[0] = new_vstep; + tfa98xx_vsteps[1] = new_vstep; + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + /* Enable internal clk (osc1m) to switch profile */ + if ((tfa98xx_dev_family(tfa98xx->handle) == 2) && (ready == 0)) { + /* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */ + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + /* Set polarity to high */ + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + + TFA_SET_BF(tfa98xx->handle, RST, 1); + TFA_SET_BF(tfa98xx->handle, SBSL, 0); + TFA_SET_BF(tfa98xx->handle, AMPC, 0); + TFA_SET_BF(tfa98xx->handle, AMPE, 0); + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1); + ready = 1; + } + + if (ready) { + err = tfa98xx_tfa_start(tfa98xx, profile, tfa98xx_vsteps); + if (err) { + pr_err("Write vstep error: %d\n", err); + } else { + pr_err("Succesfully changed vstep index!\n"); + } + } + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) { + /* Set back to external clock */ + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0); + TFA_SET_BF(tfa98xx->handle, SBSL, 1); + } + + mutex_unlock(&tfa98xx->dsp_lock); +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + } +#endif + + pr_err("vstep:%d, (control value: %d) - profile %d\n", new_vstep, + value, profile); + return (err == 0); +} + +static int tfa98xx_info_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1;// TODO handles_local[dev_idx].spkr_count + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tfacont_get_max_vstep(tfa98xx->handle, profile) - 1; + pr_err("vsteps count: %d [prof=%d]\n", tfacont_get_max_vstep(tfa98xx->handle, profile), + profile); + return 0; +} + +static int tfa98xx_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tfa98xx_mixer_profile; + return 0; +} + +static int tfa98xx_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + int profile_count = tfa98xx_mixer_profiles; + int profile = tfa98xx_mixer_profile; + int new_profile = ucontrol->value.integer.value[0]; + int err; + int ready = 0; + int prof_idx; + + + if (no_start != 0) + return 0; + + if (new_profile == profile) + return 0; + + if (new_profile >= profile_count) + return 0; + + /* get the container profile for the requested sample rate */ + prof_idx = get_profile_id_for_sr(new_profile, tfa98xx->rate); + if (prof_idx < 0) { + pr_err("tfa98xx: sample rate [%d] not supported for this mixer profile [%d].\n", tfa98xx->rate, new_profile); + return 0; + } + pr_err("selected container profile [%d]\n", prof_idx); + + /* update mixer profile */ + tfa98xx_mixer_profile = new_profile; + + /* update 'real' profile (container profile) */ + tfa98xx_profile = prof_idx; + tfa98xx_vsteps[0] = tfa98xx_prof_vsteps[prof_idx]; + tfa98xx_vsteps[1] = tfa98xx_prof_vsteps[prof_idx]; + + /* + * Don't call tfa_start() on TFA1 if there is no clock. + * For TFA2 is able to load the profile without clock. + */ + + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + /* Enable internal clk (osc1m) to switch profile */ + if (tfa98xx_dev_family(tfa98xx->handle) == 2 && ready == 0) { + /* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */ + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + /* Set polarity to high */ + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + + TFA_SET_BF(tfa98xx->handle, RST, 1); + TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 0); + TFA_SET_BF(tfa98xx->handle, AMPC, 0); + TFA_SET_BF(tfa98xx->handle, AMPE, 0); + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1); + ready = 1; + } + + if (ready) { + /* Also re-enables the interrupts */ + err = tfa98xx_tfa_start(tfa98xx, prof_idx, tfa98xx_vsteps); + if (err) { + pr_info("Write profile error: %d\n", err); + } else { + pr_err("Changed to profile %d (vstep = %d)\n", prof_idx, + tfa98xx_vsteps[0]); + } + } + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) { + /* Set back to external clock */ + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0); + TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 1); + } + + mutex_unlock(&tfa98xx->dsp_lock); + + /* Flag DSP as invalidated as the profile change may invalidate the + * current DSP configuration. That way, further stream start can + * trigger a tfa_start. + */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_INVALIDATED; + + return 1; +} + +static struct snd_kcontrol_new *tfa98xx_controls; + +/* copies the profile basename (i.e. part until .) into buf */ +static void get_profile_basename(char* buf, char* profile) +{ + int cp_len = 0, idx = 0; + char *pch; + + pch = strchr(profile, '.'); + idx = pch - profile; + cp_len = (pch != NULL) ? idx : (int) strlen(profile); + memcpy(buf, profile, cp_len); + buf[cp_len] = 0; +} + +/* return the profile name accociated with id from the profile list */ +static int get_profile_from_list(char *buf, int id) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (bprof->item_id == id) { + strcpy(buf, bprof->basename); + return 0; + } + } + + return -1; +} + +/* search for the profile in the profile list */ +static int is_profile_in_list(char *profile, int len) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (0 == strncmp(bprof->basename, profile, len)) + return 1; + } + + return 0; +} + +/* + * for the profile with id, look if the requested samplerate is + * supported, if found return the (container)profile for this + * samplerate, on error or if not found return -1 + */ +static int get_profile_id_for_sr(int id, unsigned int rate) +{ + int idx = 0; + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (id == bprof->item_id) { + idx = tfa98xx_get_fssel(rate); + if (idx < 0) { + /* samplerate not supported */ + return -1; + } + + return bprof->sr_rate_sup[idx]; + } + } + + /* profile not found */ + return -1; +} + +/* check if this profile is a calibration profile */ +static int is_calibration_profile(char *profile) +{ + if (strstr(profile, ".cal") != NULL) + return 1; + return 0; +} + +/* + * adds the (container)profile index of the samplerate found in + * the (container)profile to a fixed samplerate table in the (mixer)profile + */ +static int add_sr_to_profile(struct tfa98xx *tfa98xx, char *basename, int len, int profile) +{ + struct tfa98xx_baseprofile *bprof; + int idx = 0; + unsigned int sr = 0; + + list_for_each_entry(bprof, &profile_list, list) { + if (0 == strncmp(bprof->basename, basename, len)) { + /* add supported samplerate for this profile */ + sr = tfa98xx_get_profile_sr(tfa98xx->handle, profile); + if (!sr) { + pr_err("unable to identify supported sample rate for %s\n", bprof->basename); + return -1; + } + + /* get the index for this samplerate */ + idx = tfa98xx_get_fssel(sr); + if (idx < 0 || idx >= TFA98XX_NUM_RATES) { + pr_err("invalid index for samplerate %d\n", idx); + return -1; + } + + /* enter the (container)profile for this samplerate at the corresponding index */ + bprof->sr_rate_sup[idx] = profile; + + pr_err("added profile:samplerate = [%d:%d] for mixer profile: %s\n", profile, sr, bprof->basename); + } + } + + return 0; +} + +static int tfa98xx_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char profile_name[MAX_CONTROL_NAME] = {0}; + int count = tfa98xx_mixer_profiles, err = -1; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + err = get_profile_from_list(profile_name, uinfo->value.enumerated.item); + if (err != 0) + return -EINVAL; + + strcpy(uinfo->value.enumerated.name, profile_name); + + return 0; +} + +static int tfa98xx_get_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int tfa98xx_set_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ready = 0; + + pr_err("%ld\n", ucontrol->value.integer.value[0]); + + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + if ((ucontrol->value.integer.value[0] != 0) && ready) { + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + return 0; + mutex_lock(&tfa98xx->dsp_lock); + tfa_stop(); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } + + ucontrol->value.integer.value[0] = 0; + return 1; +} + +/*zhiguang.su@MultiMediaService,2017-04-26,add ftm spk pa rivision test*/ +static int tfa98xx_info_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + pr_err("%s\n", __func__); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 5; + + return 0; +} + +static int tfa98xx_get_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ret; + unsigned int reg; + + pr_err("%s\n", __func__); + ucontrol->value.integer.value[0] = 1; + ret = regmap_read(tfa98xx->regmap, 0x03, ®); + if (ret < 0) { + pr_err("%s Failed to read Revision register: %d\n", + __func__, ret); + ucontrol->value.integer.value[0] = 0; + } + + return 0; +} + +static int tfa98xx_set_rivision_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_err("%s\n", __func__); + ucontrol->value.integer.value[0] = 0; + return 1; +} + +static int tfa98xx_create_controls(struct tfa98xx *tfa98xx) +{ + int prof, nprof, mix_index = 0; + int nr_controls = 0, id = 0; + char *name; + struct tfa98xx_baseprofile *bprofile; + + /* Create the following controls: + * - enum control to select the active profile + * - one volume control for each profile hosting a vstep + * - Stop control on TFA1 devices + */ + + nr_controls = 1; /* Profile control */ + if (tfa98xx_dev_family(tfa98xx->handle) == 1) + nr_controls += 1; /* Stop control */ + + /* allocate the tfa98xx_controls base on the nr of profiles */ + nprof = tfaContMaxProfile(tfa98xx->handle); + + for (prof = 0; prof < nprof; prof++) { + if (tfacont_get_max_vstep(tfa98xx->handle, prof)) + nr_controls++; /* Playback Volume control */ + } + tfa98xx_controls = devm_kzalloc(tfa98xx->codec->dev, + nr_controls * sizeof(tfa98xx_controls[0]), GFP_KERNEL); + if(!tfa98xx_controls) + return -ENOMEM; + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + scnprintf(name, MAX_CONTROL_NAME, "%s Profile", tfa98xx->fw.name); + printk("tfa98xx_create_controls:name = %s\n",name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_profile; + tfa98xx_controls[mix_index].get = tfa98xx_get_profile; + tfa98xx_controls[mix_index].put = tfa98xx_set_profile; + // tfa98xx_controls[mix_index].private_value = profs; /* save number of profiles */ + mix_index++; + + /* create mixer items for each profile that has volume */ + for (prof = 0; prof < nprof; prof++) { + /* create an new empty profile */ + bprofile = devm_kzalloc(tfa98xx->codec->dev, sizeof(*bprofile), GFP_KERNEL); + if (!bprofile) + return -ENOMEM; + + bprofile->len = 0; + bprofile->item_id = -1; + INIT_LIST_HEAD(&bprofile->list); + + /* copy profile name into basename until the . */ + get_profile_basename(bprofile->basename, tfaContProfileName(tfa98xx->handle, prof)); + bprofile->len = strlen(bprofile->basename); + + /* + * search the profile list for a profile with basename, if it is not found then + * add it to the list and add a new mixer control (if it has vsteps) + * also, if it is a calibration profile, do not add it to the list + */ + if (is_profile_in_list(bprofile->basename, bprofile->len) == 0 && + is_calibration_profile(tfaContProfileName(tfa98xx->handle, prof)) == 0) { + /* the profile is not present, add it to the list */ + list_add(&bprofile->list, &profile_list); + bprofile->item_id = id++; + + pr_err("profile added [%d]: %s\n", bprofile->item_id, bprofile->basename); + + if (tfacont_get_max_vstep(tfa98xx->handle, prof)) { + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s %s Playback Volume", + tfa98xx->fw.name, bprofile->basename); + + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_vstep; + tfa98xx_controls[mix_index].get = tfa98xx_get_vstep; + tfa98xx_controls[mix_index].put = tfa98xx_set_vstep; + tfa98xx_controls[mix_index].private_value = prof; /* save profile index */ + mix_index++; + } + } + + /* look for the basename profile in the list of mixer profiles and add the + container profile index to the supported samplerates of this mixer profile */ + add_sr_to_profile(tfa98xx, bprofile->basename, bprofile->len, prof); + } + + + if (tfa98xx_dev_family(tfa98xx->handle) == 1) { + /* Create a mixer item for stop control on TFA1 */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s Stop", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = snd_soc_info_bool_ext; + tfa98xx_controls[mix_index].get = tfa98xx_get_stop_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_stop_ctl; + mix_index++; + } + + /* set the number of user selectable profiles in the mixer */ + tfa98xx_mixer_profiles = id; +/*zhiguang.su@MultiMediaService,2017-04-26,add ftm spk pa rivision test*/ + snd_soc_add_codec_controls(tfa98xx->codec, tfa98xx_at_controls, + ARRAY_SIZE(tfa98xx_at_controls)); + return snd_soc_add_codec_controls(tfa98xx->codec, + tfa98xx_controls, mix_index); +} + +static void *tfa98xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + memcpy(str, buf, strlen(buf)); + return str; +} + +static int tfa98xx_append_i2c_address(struct device *dev, + struct i2c_client *i2c, + struct snd_soc_dapm_widget *widgets, + int num_widgets, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = i2c->adapter->nr; + int addr = i2c->addr; + if (dai_drv && num_dai > 0) + for(i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x",dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = tfa98xx_devm_kstrdup(dev, buf); + } + + /* the idea behind this is convert: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + * into: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback-2-36", 0, SND_SOC_NOPM, 0, 0), + */ + if (widgets && num_widgets > 0) + for(i = 0; i < num_widgets; i++) { + if(!widgets[i].sname) + continue; + if((widgets[i].id == snd_soc_dapm_aif_in) + || (widgets[i].id == snd_soc_dapm_aif_out)) { + snprintf(buf, 50, "%s-%x-%x", widgets[i].sname, + i2cbus, addr); + widgets[i].sname = tfa98xx_devm_kstrdup(dev, buf); + } + } + + return 0; +} + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_common[] = { + /* Stream widgets */ + SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "AIF Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_INPUT("AEC Loopback"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_stereo[] = { + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_saam[] = { + SND_SOC_DAPM_INPUT("SAAM MIC"), +}; + +static struct snd_soc_dapm_widget tfa9888_dapm_inputs[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_common[] = { + { "OUTL", NULL, "AIF IN" }, + { "AIF OUT", NULL, "AEC Loopback" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_saam[] = { + { "AIF OUT", NULL, "SAAM MIC" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_stereo[] = { + { "OUTR", NULL, "AIF IN" }, +}; + +static const struct snd_soc_dapm_route tfa9888_input_dapm_routes[] = { + { "AIF OUT", NULL, "DMIC1" }, + { "AIF OUT", NULL, "DMIC2" }, + { "AIF OUT", NULL, "DMIC3" }, + { "AIF OUT", NULL, "DMIC4" }, +}; + +static void tfa98xx_add_widgets(struct tfa98xx *tfa98xx) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_codec_get_dapm(tfa98xx->codec); + struct snd_soc_dapm_widget *widgets; + unsigned int num_dapm_widgets = ARRAY_SIZE(tfa98xx_dapm_widgets_common); + + widgets = devm_kzalloc(&tfa98xx->i2c->dev, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common), + GFP_KERNEL); + if (!widgets) + return; + memcpy(widgets, tfa98xx_dapm_widgets_common, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + + tfa98xx_append_i2c_address(&tfa98xx->i2c->dev, + tfa98xx->i2c, + widgets, + num_dapm_widgets, + NULL, + 0); + + snd_soc_dapm_new_controls(dapm, widgets, + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_common, + ARRAY_SIZE(tfa98xx_dapm_routes_common)); + + if (tfa98xx->flags & TFA98XX_FLAG_STEREO_DEVICE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_stereo, + ARRAY_SIZE(tfa98xx_dapm_widgets_stereo)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_stereo, + ARRAY_SIZE(tfa98xx_dapm_routes_stereo)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_MULTI_MIC_INPUTS) { + snd_soc_dapm_new_controls(dapm, tfa9888_dapm_inputs, + ARRAY_SIZE(tfa9888_dapm_inputs)); + snd_soc_dapm_add_routes(dapm, tfa9888_input_dapm_routes, + ARRAY_SIZE(tfa9888_input_dapm_routes)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_saam, + ARRAY_SIZE(tfa98xx_dapm_widgets_saam)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_saam, + ARRAY_SIZE(tfa98xx_dapm_routes_saam)); + } +} + + +/* Match tfa98xx device structure with a valid DSP handle */ +/* TODO can be removed once we pass the device struct in stead of handles + The check in tfa98xx_register_dsp() is implicitly done in tfa_probe() /tfa98xx_cnt_slave2idx(_) +*/ +static int tfa98xx_register_dsp(struct tfa98xx *tfa98xx) +{ + int i, handle = -1; + u8 slave; + + for (i = 0; i < tfa98xx_cnt_max_device(); i++) { + if (tfaContGetSlave(i, &slave) != Tfa98xx_Error_Ok) + goto reg_err; + pr_err("%s: i=%d - dev = 0x%x\n", __func__, i, slave); + if (slave == tfa98xx->i2c->addr) { + handle = i; + break; + } + } + if (handle != -1) { + tfa98xx_devices[handle] = tfa98xx; + dev_info(&tfa98xx->i2c->dev, + "Registered DSP instance with handle %d\n", + handle); + tfa98xx_registered_handles++; + return handle; + } +reg_err: + dev_err(&tfa98xx->i2c->dev, + "Unable to match I2C address 0x%x with a container device\n", + tfa98xx->i2c->addr); + return -EINVAL; +} + +static void tfa98xx_unregister_dsp(struct tfa98xx *tfa98xx) +{ + tfa98xx_registered_handles--; + + tfa98xx_devices[tfa98xx->handle] = NULL; + dev_info(&tfa98xx->i2c->dev, "Un-registered DSP instance with handle %d\n", + tfa98xx->handle); +} + + +/* I2C wrapper functions */ +enum Tfa98xx_Error tfa98xx_write_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_write(tfa98xx->regmap, subaddress, value); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, " WR reg=0x%02x, val=0x%04x %s\n", + subaddress, value, + ret<0? "Error!!" : ""); + + if(tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\tWR reg=0x%02x, val=0x%04x %s\n", + subaddress, value, + ret<0? "Error!!" : ""); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_read_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short *val) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + unsigned int value; + int retries = I2C_RETRIES; + int ret; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_read(tfa98xx->regmap, subaddress, &value); + if (ret < 0) { + pr_warn("i2c error at subaddress 0x%x, retries left: %d\n", subaddress, retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + *val = value & 0xffff; + + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, "RD reg=0x%02x, val=0x%04x %s\n", + subaddress, *val, + ret<0? "Error!!" : ""); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\tRD reg=0x%02x, val=0x%04x %s\n", + subaddress, *val, + ret<0? "Error!!" : ""); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_read_data(Tfa98xx_handle_t handle, + unsigned char reg, + int len, unsigned char value[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + struct i2c_client *tfa98xx_client; + int err; + int tries = 0; + struct i2c_msg msgs[] = { + { + .flags = 0, + .len = 1, + .buf = ®, + }, { + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + if (tfa98xx_devices[handle] && tfa98xx_devices[handle]->i2c) { + tfa98xx = tfa98xx_devices[handle]; + tfa98xx_client = tfa98xx->i2c; + msgs[0].addr = tfa98xx_client->addr; + msgs[1].addr = tfa98xx_client->addr; + + do { + err = i2c_transfer(tfa98xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&tfa98xx_client->dev, "read transfer error %d\n", + err); + error = Tfa98xx_Error_Fail; + } + + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx_client->dev, "RD-DAT reg=0x%02x, len=%d\n", + reg, len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tRD-DAT reg=0x%02x, len=%d\n", + reg, len); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_write_raw(Tfa98xx_handle_t handle, + int len, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; +retry: + ret = i2c_master_send(tfa98xx->i2c, data, len); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + if (ret == len) { + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, " WR-RAW len=%d\n", len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tWR-RAW len=%d\n", len); + return Tfa98xx_Error_Ok; + } + pr_err(" WR-RAW (len=%d) Error I2C send size mismatch %d\n", len, ret); + error = Tfa98xx_Error_Fail; + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +/* Read and return status_reg content, and intercept (interrupt related) + * events if any. + * mask can be used to ask to ignore some status bits. + */ +static unsigned int tfa98xx_read_status_reg(struct tfa98xx *tfa98xx, + unsigned int mask) +{ + unsigned int reg; + /* interrupt bits to check */ + unsigned int errs = TFA98XX_STATUSREG_WDS | + TFA98XX_STATUSREG_SPKS; + + regmap_read(tfa98xx->regmap, TFA98XX_STATUSREG, ®); + + if (reg & errs & ~mask) { + /* interesting status bits to handle. Just trace for now. */ + dev_info(tfa98xx->codec->dev, "status_reg events: 0x%x\n", reg); + } + + return reg; +} + +/* Interrupts management */ + +static void tfa98xx_interrupt_restore_tfa2(struct tfa98xx *tfa98xx) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + /* Write interrupt enable registers */ + regmap_write(tfa98xx->regmap, base_addr_inten + 0, + handles_local[tfa98xx->handle].interrupt_enable[0]); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, + handles_local[tfa98xx->handle].interrupt_enable[1]); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, + handles_local[tfa98xx->handle].interrupt_enable[2]); +} + +static void tfa98xx_interrupt_enable_tfa2(struct tfa98xx *tfa98xx, bool enable) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + if (enable) { + tfa98xx_interrupt_restore_tfa2(tfa98xx); + } else { + regmap_write(tfa98xx->regmap, base_addr_inten + 0, 0); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, 0); + } +} + +/* Check if tap-detection can and shall be enabled. + * Configure SPK interrupt accordingly or setup polling mode + * Tap-detection shall be active if: + * - the service is enabled (tapdet_open), AND + * - the current profile is a tap-detection profile + * On TFA1 familiy of devices, activating tap-detection means enabling the SPK + * interrupt if available. + * We also update the tapdet_enabled and tapdet_poll variables. + */ +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx) +{ + unsigned int spkerr, enable = false; + unsigned int err; + int val, count = 0; + + /* Support tap-detection on TFA1 family of devices */ + if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) || + (tfa98xx_dev_family(tfa98xx->handle)) != 1) + return; + + if (tfa98xx->tapdet_open && + (tfa98xx->tapdet_profiles & (1 << tfa98xx_profile))) + enable = true; + + spkerr = enable ? 0 : 1; + + if (!gpio_is_valid(tfa98xx->irq_gpio)) { + /* interrupt not available, setup polling mode */ + tfa98xx->tapdet_poll = true; + if (enable) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->tapdet_work, HZ/10); + else + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + dev_dbg(tfa98xx->codec->dev, + "Polling for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx_profile); + + } else { + dev_dbg(tfa98xx->codec->dev, + "SPK interrupt for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx_profile); + + /* update status_reg mask to match enabled interrupts */ + handles_local[tfa98xx->handle].interrupt_status[0] &= + ~TFA98XX_STATUSREG_SPKS; + handles_local[tfa98xx->handle].interrupt_status[0] |= + enable << TFA98XX_STATUSREG_SPKS_POS; + + /* update interrupt_reg to match enabled interrupts */ + handles_local[tfa98xx->handle].interrupt_enable[0] &= + ~TFA98XX_INTERRUPT_REG_SPKD; + handles_local[tfa98xx->handle].interrupt_enable[0] |= + spkerr << TFA98XX_INTERRUPT_REG_SPKD_POS; + } + + /* check disabled => enabled transition to clear pending events */ + if (!tfa98xx->tapdet_enabled && enable) { + /* clear pending event if any */ + err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0, + Tfa98xx_DMEM_XMEM); + if (err) + pr_info("Unable to write to XMEM\n"); + + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) { + val = tfa98xx_read_status_reg(tfa98xx, + TFA98XX_STATUSREG_SPKS); + count++; + } + if (count > 1) + pr_info("Had to run %d times to ack SPKS at init\n", count); + } + + tfa98xx->tapdet_enabled = enable; + + if (!tfa98xx->tapdet_poll) + tfa98xx_interrupt_restore(tfa98xx); +} + +/* Initial configuration of interrupt masks of devices for TFA1 family + * Disable all interrupts by default. + */ +static void tfa98xx_interrupt_setup_tfa1(struct tfa98xx *tfa98xx) +{ + uint16_t ie_reg = 0; + + /* disable all interrupt sources */ + ie_reg = TFA98XX_INTERRUPT_REG_VDDD | + TFA98XX_INTERRUPT_REG_OTDD | + TFA98XX_INTERRUPT_REG_OVDD | + TFA98XX_INTERRUPT_REG_UVDD | + TFA98XX_INTERRUPT_REG_OCDD | + TFA98XX_INTERRUPT_REG_CLKD | + TFA98XX_INTERRUPT_REG_DCCD | + TFA98XX_INTERRUPT_REG_SPKD | + TFA98XX_INTERRUPT_REG_WDD; + /* preserve reserved value */ + ie_reg |= 1 << 9; + + /* Store requested setup */ + handles_local[tfa98xx->handle].interrupt_enable[0] = ie_reg; + handles_local[tfa98xx->handle].interrupt_status[0] = 0; + + dev_dbg(&tfa98xx->i2c->dev, "Initial interrupts setup: ICR = 0x%04x\n", ie_reg); +} + +/* Restore for 1st generation of devices */ +static void tfa98xx_interrupt_restore_tfa1(struct tfa98xx *tfa98xx) +{ + unsigned int ie_reg = 0; + + regmap_read(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, &ie_reg); + + if (ie_reg != handles_local[tfa98xx->handle].interrupt_enable[0]) { + ie_reg = handles_local[tfa98xx->handle].interrupt_enable[0]; + + /* Write interrupt enable registers */ + regmap_write(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, ie_reg); + + dev_dbg(&tfa98xx->i2c->dev, "Restored interrupts: ICR = 0x%04x\n", + ie_reg); + } else { + dev_dbg(&tfa98xx->i2c->dev, "No interrupt restore needed\n"); + } + +} + +/* Enable for 1st generation of devices */ +static void tfa98xx_interrupt_enable_tfa1(struct tfa98xx *tfa98xx, bool enable) +{ + handles_local[tfa98xx->handle].interrupt_enable[0] &= ~TFA98XX_INTERRUPT_REG_INT; + handles_local[tfa98xx->handle].interrupt_enable[0] |= enable << TFA98XX_INTERRUPT_REG_INT_POS; + + tfa98xx_interrupt_restore_tfa1(tfa98xx); +} + +static void tfa98xx_interrupt_setup_tfa2(struct tfa98xx *tfa98xx) +{ + uint16_t ie_reg; + + handles_local[tfa98xx->handle].interrupt_enable[0] = 0; + ie_reg = 0; + TFA_SET_BF_VALUE(tfa98xx->handle, IEMWSRC, 1, &ie_reg); + handles_local[tfa98xx->handle].interrupt_enable[1] = ie_reg; + handles_local[tfa98xx->handle].interrupt_enable[2] = 0; +} + +/* Initial SW configuration for interrupts. Does not enable HW interrupts. */ +static void tfa98xx_interrupt_setup(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV) + tfa98xx_interrupt_setup_tfa1(tfa98xx); + else + tfa98xx_interrupt_setup_tfa2(tfa98xx); +} + +/* Restore interrupt setup in case it would be lost (at device cold-start) */ +static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_interrupt_restore_tfa2(tfa98xx); + else + tfa98xx_interrupt_restore_tfa1(tfa98xx); +} + +/* global enable / disable interrupts */ +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_interrupt_enable_tfa2(tfa98xx, enable); + else + tfa98xx_interrupt_enable_tfa1(tfa98xx, enable); +} + +/* Firmware management + * Downloaded once only at module init + * FIXME: may need to review that (one per instance of codec device?) + */ +static char *fw_name = "tfa98xx.cnt"; +module_param(fw_name, charp, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(fw_name, "TFA98xx DSP firmware (container file) name."); + +static nxpTfaContainer_t *container; +#if 0 +static void tfa98xx_container_loaded(const struct firmware *cont, void *context) +{ + struct tfa98xx *tfa98xx = context; + enum tfa_error tfa_err; + int container_size; + int handle; + int ret; + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + if (!cont) { + pr_err("Failed to read %s\n", fw_name); + return; + } + + pr_err("loaded %s - size: %zu\n", fw_name, + cont ? cont->size : 0); + + container = kzalloc(cont->size, GFP_KERNEL); + if (!container) { + release_firmware(cont); + pr_err("Error allocating memory\n"); + return; + } + + container_size = cont->size; + memcpy(container, cont->data, container_size); + release_firmware(cont); + + pr_err("%.2s%.2s\n", container->version, container->subversion); + pr_err("%.8s\n", container->customer); + pr_err("%.8s\n", container->application); + pr_err("%.8s\n", container->type); + pr_err("%d ndev\n", container->ndev); + pr_err("%d nprof\n", container->nprof); + + tfa_err = tfa_load_cnt(container, container_size); + if (tfa_err != tfa_error_ok) { + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return; + } + + /* register codec with dsp */ + tfa98xx->handle = tfa98xx_register_dsp(tfa98xx); + if (tfa98xx->handle < 0) { + dev_err(tfa98xx->dev, "Cannot register with DSP, aborting\n"); + return; + } + + if (tfa_probe(tfa98xx->i2c->addr << 1, &handle) != Tfa98xx_Error_Ok) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return; + } + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->fw.name); + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strcmp(tfaContProfileName(tfa98xx->handle, i), + dflt_prof_name) == 0) { + tfa98xx_profile = i; + dev_info(tfa98xx->dev, + "changing default profile to %s (%d)\n", + dflt_prof_name, tfa98xx_profile); + break; + } + } + if (i >= tfaContMaxProfile(tfa98xx->handle)) + dev_info(tfa98xx->dev, + "Default profile override failed (%s profile not found)\n", + dflt_prof_name); + } + + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK; + pr_err("Firmware init complete\n"); + + if (no_start != 0) + return; + + /* Only controls for master device */ + if (tfa98xx->handle == 0) + tfa98xx_create_controls(tfa98xx); + + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE) { + tfa98xx_interrupt_enable(tfa98xx, true); + return; + } + + mutex_lock(&tfa98xx->dsp_lock); + + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret == Tfa98xx_Error_Ok) + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + mutex_unlock(&tfa98xx->dsp_lock); + tfa98xx_interrupt_enable(tfa98xx, true); +} +#endif +static int tfa98xx_load_container(struct tfa98xx *tfa98xx) +{ + enum tfa_error tfa_err; + int handle; + int container_size; + const struct firmware *fw; + int ret; + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + + ret = request_firmware(&fw,fw_name,tfa98xx->dev); + if(ret < 0) + { + pr_err("Failed to read %s\n", fw_name); + return ret; + } + + pr_err("loaded %s - size: %zu\n", fw_name, + fw ? fw->size : 0); + + container = kzalloc(fw->size, GFP_KERNEL); + if (!container) { + release_firmware(fw); + pr_err("Error allocating memory\n"); + return -ENOMEM; + } + container_size = fw->size; + memcpy(container, fw->data, container_size); + release_firmware(fw); + + pr_err("%.2s%.2s\n", container->version, container->subversion); + pr_err("%.8s\n", container->customer); + pr_err("%.8s\n", container->application); + pr_err("%.8s\n", container->type); + pr_err("%d ndev\n", container->ndev); + pr_err("%d nprof\n", container->nprof); + + tfa_err = tfa_load_cnt(container, container_size); + if (tfa_err != tfa_error_ok) { + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return tfa_err; + } + + /* register codec with dsp */ + tfa98xx->handle = tfa98xx_register_dsp(tfa98xx); + if (tfa98xx->handle < 0) { + dev_err(tfa98xx->dev, "Cannot register with DSP, aborting\n"); + return tfa98xx->handle; + } + + if (tfa_probe(tfa98xx->i2c->addr << 1, &handle) != Tfa98xx_Error_Ok) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return tfa98xx->i2c->addr; + } + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->fw.name); + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strcmp(tfaContProfileName(tfa98xx->handle, i), + dflt_prof_name) == 0) { + tfa98xx_profile = i; + dev_info(tfa98xx->dev, + "changing default profile to %s (%d)\n", + dflt_prof_name, tfa98xx_profile); + break; + } + } + if (i >= tfaContMaxProfile(tfa98xx->handle)) + dev_info(tfa98xx->dev, + "Default profile override failed (%s profile not found)\n", + dflt_prof_name); + } + + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK; + pr_err("Firmware init complete\n"); + + if (no_start != 0) + return no_start; + + /* Only controls for master device */ + if (tfa98xx->handle == 0) + tfa98xx_create_controls(tfa98xx); + + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE) { + tfa98xx_interrupt_enable(tfa98xx, true); + return 0; + } + + mutex_lock(&tfa98xx->dsp_lock); + + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret == Tfa98xx_Error_Ok) + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + mutex_unlock(&tfa98xx->dsp_lock); + tfa98xx_interrupt_enable(tfa98xx, true); + //return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + // fw_name, tfa98xx->dev, GFP_KERNEL, + // tfa98xx, tfa98xx_container_loaded); + return 0; +} + + +static void tfa98xx_tapdet(struct tfa98xx *tfa98xx) +{ + unsigned int mem; + int err, btn, count = 0; + uint16_t val; + + /* check tap pattern (BTN_0 is "error" wrong tap indication */ + tfa98xx_dsp_read_mem(tfa98xx->handle, XMEM_TAP_READ, 1, &mem); + switch (mem) { + case 0xffffffff: + pr_info("More than 4 taps detected! (flagTapPattern = -1)\n"); + btn = BTN_0; + break; + case 0xfffffffe: + pr_info("Single tap detected! (flagTapPattern = -2)\n"); + btn = BTN_0; + break; + case 0: + pr_info("Unrecognized pattern! (flagTapPattern = 0)\n"); + btn = BTN_0; + break; + default: + pr_info("Detected pattern: %d\n", mem); + btn = BTN_0 + mem; + break; + } + + input_report_key(tfa98xx->input, btn, 1); + input_report_key(tfa98xx->input, btn, 0); + input_sync(tfa98xx->input); + + /* acknowledge event */ + err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0, Tfa98xx_DMEM_XMEM); + if (err) + pr_info("Unable to write to XMEM\n"); + + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) { + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + count++; + } + if (count > 1) + pr_info("Had to run %d times to ack SPKS\n", count); + +} + +static void tfa98xx_tapdet_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + u16 val; + + tfa98xx = container_of(work, struct tfa98xx, tapdet_work.work); + + /* Check for SPKS bit*/ + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG); + + if (val & TFA98XX_STATUSREG_SPKS) + tfa98xx_tapdet(tfa98xx); + + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->tapdet_work, HZ/10); +} + + + +static void tfa98xx_dsp_init(struct tfa98xx *tfa98xx) +{ + int ret; + bool failed = false; + bool reschedule = false; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + pr_err("Skipping tfa_start (no FW: %d)\n", tfa98xx->dsp_fw_state); + return; + } + + if(tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + pr_err("Stream already started, skipping DSP power-on\n"); + return; + } + + mutex_lock(&tfa98xx->dsp_lock); + + tfa98xx->dsp_init = TFA98XX_DSP_INIT_PENDING; + + if (tfa98xx->init_count < TF98XX_MAX_DSP_START_TRY_COUNT) { + /* directly try to start DSP */ + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret != Tfa98xx_Error_Ok) { + /* It may fail as we may not have a valid clock at that + * time, so re-schedule and re-try later. + */ + dev_err(&tfa98xx->i2c->dev, + "tfa_start failed! (err %d) - %d\n", + ret, tfa98xx->init_count); + reschedule = true; + } else { + /* Subsystem ready, tfa init complete */ + pr_debug("tfa_start success (%d)\n", + tfa98xx->init_count); + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + /* + * start monitor thread to check IC status bit + * periodically, and re-init IC to recover if + * needed. + */ + } + } else { + /* exceeded max number ot start tentatives, cancel start */ + dev_err(&tfa98xx->i2c->dev, + "Failed starting device (%d)\n", + tfa98xx->init_count); + failed = true; + } + if (reschedule) { + /* reschedule this init work for later */ + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, + msecs_to_jiffies(5)); + tfa98xx->init_count++; + } + if (failed) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_FAIL; + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + } + mutex_unlock(&tfa98xx->dsp_lock); + return; +} + +/*zhiguang.su@MultiMediaService,2017-02-09,avoid no sound for ftm*/ +static void tfa98xx_dsp_startInit(struct tfa98xx *tfa98xx) +{ + /* Only do dsp init for master device */ + if (tfa98xx->handle != 0) + return; + + tfa98xx_dsp_init(tfa98xx); +} + + +static void tfa98xx_dsp_init_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, init_work.work); + + /* Only do dsp init for master device */ + if (tfa98xx->handle != 0) + return; + + tfa98xx_dsp_init(tfa98xx); +} + +static void tfa98xx_interrupt(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, interrupt_work.work); + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8; + unsigned int base_addr_icl = TFA_FAM(tfa98xx->handle,ICLVDDS) >> 8; + //unsigned int base_addr_ipo = TFA_FAM(tfa98xx->handle,IPOVDDS) >> 8; + + u32 out1, out2, out3; + + pr_info("\n"); + + regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1); + regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2); + regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3); + + out1 &= handles_local[tfa98xx->handle].interrupt_enable[0]; + out2 &= handles_local[tfa98xx->handle].interrupt_enable[1]; + out3 &= handles_local[tfa98xx->handle].interrupt_enable[2]; + + if (out1) { + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 0, out1); + regmap_write(tfa98xx->regmap, base_addr_inten + 0, + handles_local[tfa98xx->handle].interrupt_enable[0]); + } + + if (out2) { + /* manager wait for source state */ + if (TFA_GET_BF_VALUE(tfa98xx->handle, ISTMWSRC, out2) > 0) { + int manwait1 = TFA_GET_BF(tfa98xx->handle, MANWAIT1); + + if (manwait1 > 0) { + pr_info("entering wait for source state\n"); + tfa98xx->count_wait_for_source_state++; + + /* set AMPC and AMPE to make sure the amp is enabled */ + pr_info("setting AMPC and AMPE to 1 (default) \n"); + TFA_SET_BF(tfa98xx->handle, AMPC, 1); + TFA_SET_BF(tfa98xx->handle, AMPE, 1); + + /* set MANSCONF here, the manager will continue if clock is there */ + TFA_SET_BF(tfa98xx->handle, MANSCONF, 1); + } else { + /* Now we can switch profile with internal clock it is not required to call tfa_start */ + + pr_info("leaving wait for source state\n"); + + TFA_SET_BF(tfa98xx->handle, MANSCONF, 0); + } + + if (manwait1 > 0) + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 0); + else + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + } + + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 1, out2); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, + handles_local[tfa98xx->handle].interrupt_enable[1]); + } + + if (out3) { + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 2, out3); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, + handles_local[tfa98xx->handle].interrupt_enable[2]); + } + +} + +static int tfa98xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int sr; + int len, prof, nprof = tfaContMaxProfile(tfa98xx->handle), idx = 0; + char *basename; + + /* + * Support CODEC to CODEC links, + * these are called with a NULL runtime pointer. + */ + if (!substream->runtime) + return 0; + + if (no_start != 0) + return 0; + + basename = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!basename) + return -ENOMEM; + + /* copy profile name into basename until the . */ + get_profile_basename(basename, tfaContProfileName(tfa98xx->handle, tfa98xx_profile)); + len = strlen(basename); + + /* loop over all profiles and get the supported samples rate(s) from + * the profiles with the same basename + */ + for (prof = 0; prof < nprof; prof++) { + if (0 == strncmp(basename, tfaContProfileName(tfa98xx->handle, prof), len)) { + /* Check which sample rate is supported with current profile, + * and enforce this. + */ + sr = tfa98xx_get_profile_sr(tfa98xx->handle, prof); + if (!sr) + dev_info(codec->dev, "Unable to identify supported sample rate\n"); + tfa98xx->rate_constraint_list[idx++] = sr; + tfa98xx->rate_constraint.count += 1; + } + } + +/*zhiguang.su@MultiMediaService,2017-02-09,changed for NXP advise,avoid 8k tinyplay problem*/ + return 0; + #if 0 + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &tfa98xx->rate_constraint); + #endif + +} + +static int tfa98xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec_dai->codec); + + tfa98xx->sysclk = freq; + return 0; +} + +static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(dai->codec); + struct snd_soc_codec *codec = dai->codec; + + pr_err("fmt=0x%x\n", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid Codec master mode\n"); + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_PDM: + break; + default: + dev_err(codec->dev, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + tfa98xx->audio_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int tfa98xx_get_fssel(unsigned int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(rate_to_fssel); i++) { + if (rate_to_fssel[i].rate == rate) { + return rate_to_fssel[i].fssel; + } + } + return -EINVAL; +} + +static int tfa98xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int rate; + int prof_idx; + + /* Supported */ + rate = params_rate(params); + pr_debug("Requested rate: %d, sample size: %d, physical size: %d\n", + rate, snd_pcm_format_width(params_format(params)), + snd_pcm_format_physical_width(params_format(params))); + + if (params_channels(params) > 2) { + pr_warn("Unusual number of channels: %d\n", params_channels(params)); + } + + if (no_start != 0) + return 0; + + /* check if samplerate is supported for this mixer profile */ + prof_idx = get_profile_id_for_sr(tfa98xx_mixer_profile, rate); + if (prof_idx < 0) { + pr_err("tfa98xx: invalid sample rate %d.\n", rate); + return -EINVAL; + } + pr_debug("mixer profile:container profile = [%d:%d]\n", + tfa98xx_mixer_profile, prof_idx); + + + /* update 'real' profile (container profile) */ + tfa98xx_profile = prof_idx; + + /* update to new rate */ + tfa98xx->rate = rate; + + return 0; +} + +static int tfa98xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + dev_dbg(&tfa98xx->i2c->dev, "state: %d\n", mute); + + if (!(tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE)) + return 0; + + if (no_start) { + pr_err("no_start parameter set no tfa_start or tfa_stop, returning\n"); + return 0; + } + + if (mute) { + /* stop DSP only when both playback and capture streams + * are deactivated + */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tfa98xx->pstream = 0; + else + tfa98xx->cstream = 0; + if (tfa98xx->pstream != 0 || tfa98xx->cstream != 0) + return 0; + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + return 0; + mutex_lock(&tfa98xx->dsp_lock); + tfa_stop(); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tfa98xx->pstream = 1; + else + tfa98xx->cstream = 1; +/*zhiguang.su@MultiMediaService,2017-02-09,avoid no sound for ftm*/ + if(!tfa98xx->startInit) + { + tfa98xx->startInit = true; + tfa98xx_dsp_startInit(tfa98xx); + } + else + { + /* Start DSP */ + if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, + 0); + } + } + + return 0; +} + +static const struct snd_soc_dai_ops tfa98xx_dai_ops = { + .startup = tfa98xx_startup, + .set_fmt = tfa98xx_set_fmt, + .set_sysclk = tfa98xx_set_dai_sysclk, + .hw_params = tfa98xx_hw_params, + .mute_stream = tfa98xx_mute, +}; + +static struct snd_soc_dai_driver tfa98xx_dai[] = { + { + .name = "tfa98xx_codec", + .base = TFA98XX_TDM_CONFIG0 - 1, + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + /*.capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + },*/ + .ops = &tfa98xx_dai_ops, + .symmetric_rates = 0, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) + .symmetric_channels = 0, + .symmetric_samplebits = 0, +#endif + }, +}; + +static int tfa98xx_probe(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ret; + + pr_err("\n"); + + tfa98xx->rate_constraint.list = &tfa98xx->rate_constraint_list[0]; + tfa98xx->rate_constraint.count = + ARRAY_SIZE(tfa98xx->rate_constraint_list); + + /* setup work queue, will be used to initial DSP on first boot up */ + tfa98xx->tfa98xx_wq = create_singlethread_workqueue("tfa98xx"); + if (!tfa98xx->tfa98xx_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&tfa98xx->init_work, tfa98xx_dsp_init_work); + + + INIT_DELAYED_WORK(&tfa98xx->interrupt_work, tfa98xx_interrupt); + INIT_DELAYED_WORK(&tfa98xx->tapdet_work, tfa98xx_tapdet_work); + + tfa98xx->codec = codec; + + ret = tfa98xx_load_container(tfa98xx); + pr_err("Container loading requested: %d\n", ret); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + codec->control_data = tfa98xx->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } +#endif + tfa98xx_add_widgets(tfa98xx); + + dev_info(codec->dev, "tfa98xx codec registered (%s)", + tfa98xx->fw.name); +/*zhiguang.su@MultiMediaService,2017-02-09,avoid no sound for ftm*/ +tfa98xx->startInit = false; + + g_tfa98xx = tfa98xx; + return ret; +} + +static int tfa98xx_remove(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + pr_err("\n"); + + tfa98xx_inputdev_unregister(tfa98xx); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + if (tfa98xx->tfa98xx_wq) + destroy_workqueue(tfa98xx->tfa98xx_wq); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) +struct regmap *tfa98xx_get_regmap(struct device *dev) +{ + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + return tfa98xx->regmap; +} +#endif +static struct snd_soc_codec_driver soc_codec_dev_tfa98xx = { + .probe = tfa98xx_probe, + .remove = tfa98xx_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + .get_regmap = tfa98xx_get_regmap, +#endif +}; + + +static bool tfa98xx_writeable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_readable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_volatile_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static const struct regmap_config tfa98xx_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = TFA98XX_MAX_REGISTER, + .writeable_reg = tfa98xx_writeable_register, + .readable_reg = tfa98xx_readable_register, + .volatile_reg = tfa98xx_volatile_register, + .cache_type = REGCACHE_NONE, +}; + + +static void tfa98xx_irq_tfa2(struct tfa98xx *tfa98xx) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8; + u32 en1, en2, en3; + u32 out1 = 0, out2 = 0, out3 = 0; + + pr_info("\n"); + + regmap_read(tfa98xx->regmap, base_addr_inten + 0, &en1); + regmap_read(tfa98xx->regmap, base_addr_inten + 1, &en2); + regmap_read(tfa98xx->regmap, base_addr_inten + 2, &en3); + + regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1); + regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2); + regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3); + + pr_info("interrupt1: 0x%.4x (enabled: 0x%.4x)\n", out1, en1); + pr_info("interrupt2: 0x%.4x (enabled: 0x%.4x)\n", out2, en2); + pr_info("interrupt3: 0x%.4x (enabled: 0x%.4x)\n", out3, en3); + + out1 &= en1; + out2 &= en2; + out3 &= en3; + + en1 = handles_local[tfa98xx->handle].interrupt_enable[0] ^ out1; + en2 = handles_local[tfa98xx->handle].interrupt_enable[1] ^ out2; + en3 = handles_local[tfa98xx->handle].interrupt_enable[2] ^ out3; + + regmap_write(tfa98xx->regmap, base_addr_inten + 0, en1); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, en2); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, en3); + + if (out1 || out2 || out3) + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->interrupt_work, 0); +} + +static void __tfa98xx_irq(struct tfa98xx *tfa98xx) +{ + uint16_t val; + uint16_t ie = handles_local[tfa98xx->handle].interrupt_status[0]; + + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG); + + dev_info(&tfa98xx->i2c->dev, "interrupt: 0x%04x (enabled: 0x%04x)\n", val, ie); +#ifdef DEBUG + if (!(val & ie)) { + unsigned int ireg; + /* interrupt triggered while all interrupt sources supposedly + * disabled + */ + ireg = snd_soc_read(tfa98xx->codec, TFA98XX_INTERRUPT_REG); + dev_dbg(&tfa98xx->i2c->dev, "ICR: 0x%04x\n", ireg); + } +#endif + + val &= ie; + + /* Check for SPKS bit */ + if (val & TFA98XX_STATUSREG_SPKS) + tfa98xx_tapdet(tfa98xx); +} + +static irqreturn_t tfa98xx_irq(int irq, void *data) +{ + struct tfa98xx *tfa98xx = data; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_irq_tfa2(tfa98xx); + else + __tfa98xx_irq(tfa98xx); + + return IRQ_HANDLED; +} + +static int tfa98xx_ext_reset(struct tfa98xx *tfa98xx) +{ + if (tfa98xx && gpio_is_valid(tfa98xx->reset_gpio)) { + gpio_set_value_cansleep(tfa98xx->reset_gpio, 1); + gpio_set_value_cansleep(tfa98xx->reset_gpio, 0); + } + return 0; +} + +static int tfa98xx_parse_dt(struct device *dev, struct tfa98xx *tfa98xx, + struct device_node *np) { + tfa98xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (tfa98xx->reset_gpio < 0) + dev_dbg(dev, "No reset GPIO provided, will not HW reset device\n"); + + tfa98xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (tfa98xx->irq_gpio < 0) + dev_dbg(dev, "No IRQ GPIO provided.\n"); + + return 0; +} + +static ssize_t tfa98xx_reg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + if (count != 1) { + pr_err("invalid register address"); + return -EINVAL; + } + + tfa98xx->reg = buf[0]; + + return 1; +} + +static ssize_t tfa98xx_rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + u8 *data; + int ret; + int retries = I2C_RETRIES; + + data = kmalloc(count+1, GFP_KERNEL); + if (data == NULL) { + pr_err("can not allocate memory\n"); + return -ENOMEM; + } + + data[0] = tfa98xx->reg; + memcpy(&data[1], buf, count); + +retry: + ret = i2c_master_send(tfa98xx->i2c, data, count+1); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + kfree(data); + return ret; +} + +static ssize_t tfa98xx_rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + struct i2c_msg msgs[] = { + { + .addr = tfa98xx->i2c->addr, + .flags = 0, + .len = 1, + .buf = &tfa98xx->reg, + }, + { + .addr = tfa98xx->i2c->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }, + }; + int ret; + int retries = I2C_RETRIES; +retry: + ret = i2c_transfer(tfa98xx->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return ret; + } + /* ret contains the number of i2c messages send */ + return 1 + ((ret > 1) ? count : 0); +} + +static struct bin_attribute dev_attr_rw = { + .attr = { + .name = "rw", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = tfa98xx_rw_read, + .write = tfa98xx_rw_write, +}; + +static struct bin_attribute dev_attr_reg = { + .attr = { + .name = "reg", + .mode = S_IWUSR, + }, + .size = 0, + .read = NULL, + .write = tfa98xx_reg_write, +}; + +static int tfa98xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct tfa98xx *tfa98xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags; + unsigned int reg; + int ret; + + pr_info("%s\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + tfa98xx = devm_kzalloc(&i2c->dev, sizeof(struct tfa98xx), + GFP_KERNEL); + if (tfa98xx == NULL) + return -ENOMEM; + + tfa98xx->dev = &i2c->dev; + tfa98xx->i2c = i2c; + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + tfa98xx->rate = 48000; /* init to the default sample rate (48kHz) */ + + tfa98xx->regmap = devm_regmap_init_i2c(i2c, &tfa98xx_regmap); + if (IS_ERR(tfa98xx->regmap)) { + ret = PTR_ERR(tfa98xx->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err; + } + + i2c_set_clientdata(i2c, tfa98xx); + mutex_init(&tfa98xx->dsp_lock); + init_waitqueue_head(&tfa98xx->wq); + + if (np) { + ret = tfa98xx_parse_dt(&i2c->dev, tfa98xx, np); + if (ret) { + dev_err(&i2c->dev, "Failed to parse DT node\n"); + goto err; + } + if (no_start) + tfa98xx->irq_gpio = -1; + } else { + tfa98xx->reset_gpio = -1; + tfa98xx->irq_gpio = -1; + } + + if (gpio_is_valid(tfa98xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "TFA98XX_RST"); + if (ret) + goto err; + } + + if (gpio_is_valid(tfa98xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->irq_gpio, + GPIOF_DIR_IN, "TFA98XX_INT"); + if (ret) + goto err; + } + + /* Power up! */ + tfa98xx_ext_reset(tfa98xx); + + if (no_start == 0) { + ret = regmap_read(tfa98xx->regmap, 0x03, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read Revision register: %d\n", + ret); + return -EIO; + } + switch (reg & 0xff) { + case 0x88: /* tfa9888 */ + pr_info("TFA9888 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_STEREO_DEVICE; + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + break; + case 0x80: /* tfa9890 */ + case 0x81: /* tfa9890 */ + pr_info("TFA9890 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV; + break; + case 0x92: /* tfa9891 */ + pr_info("TFA9891 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SAAM_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_TAPDET_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV; + break; + case 0x97: + pr_info("TFA9897 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TFA9897_FAM_DEV; + break; + default: + pr_info("Unsupported device revision (0x%x)\n", reg & 0xff); + return -EINVAL; + } + } + + /* Modify the stream names, by appending the i2c device address. + * This is used with multicodec, in order to discriminate the devices. + * Stream names appear in the dai definition and in the stream . + * We create copies of original structures because each device will + * have its own instance of this structure, with its own address. + */ + dai = devm_kzalloc(&i2c->dev, sizeof(tfa98xx_dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + memcpy(dai, tfa98xx_dai, sizeof(tfa98xx_dai)); + + tfa98xx_append_i2c_address(&i2c->dev, + i2c, + NULL, + 0, + dai, + ARRAY_SIZE(tfa98xx_dai)); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_tfa98xx, dai, + ARRAY_SIZE(tfa98xx_dai)); + + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register TFA98xx: %d\n", ret); + goto err_off; + } + + if (gpio_is_valid(tfa98xx->irq_gpio) && + !(tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(tfa98xx->irq_gpio), + NULL, tfa98xx_irq, irq_flags, + "tfa98xx", tfa98xx); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + gpio_to_irq(tfa98xx->irq_gpio), ret); + goto err_off; + } + tfa98xx_interrupt_setup(tfa98xx); + } else { + dev_info(&i2c->dev, "Skipping IRQ registration\n"); + /* disable feature support if gpio was invalid */ + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + } + +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for bob power*/ + pr_err("%s request bob power\n", __func__); + bob_power = NULL; + bob_power = regulator_get(&i2c->dev, "bob_power"); + if (IS_ERR(bob_power)) + pr_err("%s request bob power error!\n", __func__); + + +#ifdef CONFIG_DEBUG_FS + tfa98xx_debug_init(tfa98xx, i2c); +#endif + /* Register the sysfs files for climax backdoor access */ + ret = device_create_bin_file(&i2c->dev, &dev_attr_rw); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + ret = device_create_bin_file(&i2c->dev, &dev_attr_reg); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + + pr_info("%s Probe completed successfully!\n", __func__); + + ret = sysfs_create_file(&i2c->dev.kobj, &tfa98xx_state_attr.attr); + if(ret < 0) + { + pr_err("%s sysfs_create_file tfa98xx_state_attr err.",__func__); + } + ret = sysfs_create_file(&i2c->dev.kobj, &tfa98xx_Log_state_attr.attr); + if(ret < 0) + { + pr_err("%s sysfs_create_file tfa98xx_Log_state_attr err.",__func__); + } + + return 0; + +err_off: + tfa98xx_unregister_dsp(tfa98xx); +err: + return ret; +} + +static int tfa98xx_i2c_remove(struct i2c_client *i2c) +{ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + pr_err("\n"); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + device_remove_bin_file(&i2c->dev, &dev_attr_reg); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); +#ifdef CONFIG_DEBUG_FS + tfa98xx_debug_remove(tfa98xx); +#endif + + tfa98xx_unregister_dsp(tfa98xx); + + snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(tfa98xx->irq_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->irq_gpio); + if (gpio_is_valid(tfa98xx->reset_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->reset_gpio); + + return 0; +} + +static const struct i2c_device_id tfa98xx_i2c_id[] = { + { "tfa98xx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tfa98xx_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id tfa98xx_dt_match[] = { + { .compatible = "nxp,tfa98xx" }, + { .compatible = "nxp,tfa9890" }, + { .compatible = "nxp,tfa9891" }, + { .compatible = "nxp,tfa9888" }, + { }, +}; +#endif + +static struct i2c_driver tfa98xx_i2c_driver = { + .driver = { + .name = "tfa98xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tfa98xx_dt_match), + }, + .probe = tfa98xx_i2c_probe, + .remove = tfa98xx_i2c_remove, + .id_table = tfa98xx_i2c_id, +}; + +static int trace_level = 1; +module_param(trace_level, int, S_IRUGO); +MODULE_PARM_DESC(trace_level, "TFA98xx debug trace level (0=off, bits:1=verbose,2=regdmesg,3=regftrace)."); +static int __init tfa98xx_i2c_init(void) +{ + int ret = 0; + + pr_info("TFA98XX driver version %s\n", TFA98XX_VERSION); + + /* Enable debug traces */ + tfa_verbose(trace_level); + tfa98xx_kmsg_regs = trace_level & 2; + tfa98xx_ftrace_regs = trace_level & 4; + + ret = i2c_add_driver(&tfa98xx_i2c_driver); + + return ret; +} +module_init(tfa98xx_i2c_init); + + +static void __exit tfa98xx_i2c_exit(void) +{ + i2c_del_driver(&tfa98xx_i2c_driver); + + kfree(container); +} +module_exit(tfa98xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC TFA98XX driver"); +MODULE_LICENSE("GPL"); + + + diff --git a/sound/soc/codecs/tfa9891/tfa98xx_genregs_N1C.h b/sound/soc/codecs/tfa9891/tfa98xx_genregs_N1C.h new file mode 100755 index 000000000000..0064137fa730 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa98xx_genregs_N1C.h @@ -0,0 +1,3853 @@ +/** Filename: Tfa98xx_genregs.h + * This file was generated automatically on 09/01/15 at 09:40:23. + * Source file: TFA9888_N1C_I2C_regmap_V1.xlsx + */ + +#ifndef TFA2_GENREGS_H +#define TFA2_GENREGS_H + + +#define TFA98XX_SYS_CONTROL0 0x00 +#define TFA98XX_SYS_CONTROL1 0x01 +#define TFA98XX_SYS_CONTROL2 0x02 +#define TFA98XX_DEVICE_REVISION 0x03 +#define TFA98XX_CLOCK_CONTROL 0x04 +#define TFA98XX_CLOCK_GATING_CONTROL 0x05 +#define TFA98XX_SIDE_TONE_CONFIG 0x0d +#define TFA98XX_CTRL_DIGTOANA_REG 0x0e +#define TFA98XX_STATUS_FLAGS0 0x10 +#define TFA98XX_STATUS_FLAGS1 0x11 +#define TFA98XX_STATUS_FLAGS2 0x12 +#define TFA98XX_STATUS_FLAGS3 0x13 +#define TFA98XX_STATUS_FLAGS4 0x14 +#define TFA98XX_BATTERY_VOLTAGE 0x15 +#define TFA98XX_TEMPERATURE 0x16 +#define TFA98XX_TDM_CONFIG0 0x20 +#define TFA98XX_TDM_CONFIG1 0x21 +#define TFA98XX_TDM_CONFIG2 0x22 +#define TFA98XX_TDM_CONFIG3 0x23 +#define TFA98XX_TDM_CONFIG4 0x24 +#define TFA98XX_TDM_CONFIG5 0x25 +#define TFA98XX_TDM_CONFIG6 0x26 +#define TFA98XX_TDM_CONFIG7 0x27 +#define TFA98XX_TDM_CONFIG8 0x28 +#define TFA98XX_TDM_CONFIG9 0x29 +#define TFA98XX_PDM_CONFIG0 0x31 +#define TFA98XX_PDM_CONFIG1 0x32 +#define TFA98XX_HAPTIC_DRIVER_CONFIG 0x33 +#define TFA98XX_GPIO_DATAIN_REG 0x34 +#define TFA98XX_GPIO_CONFIG 0x35 +#define TFA98XX_INTERRUPT_OUT_REG1 0x40 +#define TFA98XX_INTERRUPT_OUT_REG2 0x41 +#define TFA98XX_INTERRUPT_OUT_REG3 0x42 +#define TFA98XX_INTERRUPT_IN_REG1 0x44 +#define TFA98XX_INTERRUPT_IN_REG2 0x45 +#define TFA98XX_INTERRUPT_IN_REG3 0x46 +#define TFA98XX_INTERRUPT_ENABLE_REG1 0x48 +#define TFA98XX_INTERRUPT_ENABLE_REG2 0x49 +#define TFA98XX_INTERRUPT_ENABLE_REG3 0x4a +#define TFA98XX_STATUS_POLARITY_REG1 0x4c +#define TFA98XX_STATUS_POLARITY_REG2 0x4d +#define TFA98XX_STATUS_POLARITY_REG3 0x4e +#define TFA98XX_BAT_PROT_CONFIG 0x50 +#define TFA98XX_AUDIO_CONTROL 0x51 +#define TFA98XX_AMPLIFIER_CONFIG 0x52 +#define TFA98XX_AUDIO_CONTROL2 0x5a +#define TFA98XX_DCDC_CONTROL0 0x70 +#define TFA98XX_CF_CONTROLS 0x90 +#define TFA98XX_CF_MAD 0x91 +#define TFA98XX_CF_MEM 0x92 +#define TFA98XX_CF_STATUS 0x93 +#define TFA98XX_MTPKEY2_REG 0xa1 +#define TFA98XX_MTP_STATUS 0xa2 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL 0xa3 +#define TFA98XX_MTP_DATA_OUT_MSB 0xa5 +#define TFA98XX_MTP_DATA_OUT_LSB 0xa6 +#define TFA98XX_TEMP_SENSOR_CONFIG 0xb1 +#define TFA98XX_KEY2_PROTECTED_MTP0 0xf0 +#define TFA98XX_KEY1_PROTECTED_MTP4 0xf4 +#define TFA98XX_KEY1_PROTECTED_MTP5 0xf5 + +/* + * (0x00)-sys_control0 + */ + +/* + * powerdown + */ +#define TFA98XX_SYS_CONTROL0_PWDN (0x1<<0) +#define TFA98XX_SYS_CONTROL0_PWDN_POS 0 +#define TFA98XX_SYS_CONTROL0_PWDN_LEN 1 +#define TFA98XX_SYS_CONTROL0_PWDN_MAX 1 +#define TFA98XX_SYS_CONTROL0_PWDN_MSK 0x1 + +/* + * reset + */ +#define TFA98XX_SYS_CONTROL0_I2CR (0x1<<1) +#define TFA98XX_SYS_CONTROL0_I2CR_POS 1 +#define TFA98XX_SYS_CONTROL0_I2CR_LEN 1 +#define TFA98XX_SYS_CONTROL0_I2CR_MAX 1 +#define TFA98XX_SYS_CONTROL0_I2CR_MSK 0x2 + +/* + * enbl_coolflux + */ +#define TFA98XX_SYS_CONTROL0_CFE (0x1<<2) +#define TFA98XX_SYS_CONTROL0_CFE_POS 2 +#define TFA98XX_SYS_CONTROL0_CFE_LEN 1 +#define TFA98XX_SYS_CONTROL0_CFE_MAX 1 +#define TFA98XX_SYS_CONTROL0_CFE_MSK 0x4 + +/* + * enbl_amplifier + */ +#define TFA98XX_SYS_CONTROL0_AMPE (0x1<<3) +#define TFA98XX_SYS_CONTROL0_AMPE_POS 3 +#define TFA98XX_SYS_CONTROL0_AMPE_LEN 1 +#define TFA98XX_SYS_CONTROL0_AMPE_MAX 1 +#define TFA98XX_SYS_CONTROL0_AMPE_MSK 0x8 + +/* + * enbl_boost + */ +#define TFA98XX_SYS_CONTROL0_DCA (0x1<<4) +#define TFA98XX_SYS_CONTROL0_DCA_POS 4 +#define TFA98XX_SYS_CONTROL0_DCA_LEN 1 +#define TFA98XX_SYS_CONTROL0_DCA_MAX 1 +#define TFA98XX_SYS_CONTROL0_DCA_MSK 0x10 + +/* + * coolflux_configured + */ +#define TFA98XX_SYS_CONTROL0_SBSL (0x1<<5) +#define TFA98XX_SYS_CONTROL0_SBSL_POS 5 +#define TFA98XX_SYS_CONTROL0_SBSL_LEN 1 +#define TFA98XX_SYS_CONTROL0_SBSL_MAX 1 +#define TFA98XX_SYS_CONTROL0_SBSL_MSK 0x20 + +/* + * sel_enbl_amplifier + */ +#define TFA98XX_SYS_CONTROL0_AMPC (0x1<<6) +#define TFA98XX_SYS_CONTROL0_AMPC_POS 6 +#define TFA98XX_SYS_CONTROL0_AMPC_LEN 1 +#define TFA98XX_SYS_CONTROL0_AMPC_MAX 1 +#define TFA98XX_SYS_CONTROL0_AMPC_MSK 0x40 + +/* + * int_pad_io + */ +#define TFA98XX_SYS_CONTROL0_INTP (0x3<<7) +#define TFA98XX_SYS_CONTROL0_INTP_POS 7 +#define TFA98XX_SYS_CONTROL0_INTP_LEN 2 +#define TFA98XX_SYS_CONTROL0_INTP_MAX 3 +#define TFA98XX_SYS_CONTROL0_INTP_MSK 0x180 + +/* + * fs_pulse_sel + */ +#define TFA98XX_SYS_CONTROL0_FSSSEL (0x3<<9) +#define TFA98XX_SYS_CONTROL0_FSSSEL_POS 9 +#define TFA98XX_SYS_CONTROL0_FSSSEL_LEN 2 +#define TFA98XX_SYS_CONTROL0_FSSSEL_MAX 3 +#define TFA98XX_SYS_CONTROL0_FSSSEL_MSK 0x600 + +/* + * bypass_ocp + */ +#define TFA98XX_SYS_CONTROL0_BYPOCP (0x1<<11) +#define TFA98XX_SYS_CONTROL0_BYPOCP_POS 11 +#define TFA98XX_SYS_CONTROL0_BYPOCP_LEN 1 +#define TFA98XX_SYS_CONTROL0_BYPOCP_MAX 1 +#define TFA98XX_SYS_CONTROL0_BYPOCP_MSK 0x800 + +/* + * test_ocp + */ +#define TFA98XX_SYS_CONTROL0_TSTOCP (0x1<<12) +#define TFA98XX_SYS_CONTROL0_TSTOCP_POS 12 +#define TFA98XX_SYS_CONTROL0_TSTOCP_LEN 1 +#define TFA98XX_SYS_CONTROL0_TSTOCP_MAX 1 +#define TFA98XX_SYS_CONTROL0_TSTOCP_MSK 0x1000 + + +/* + * (0x01)-sys_control1 + */ + +/* + * vamp_sel + */ +#define TFA98XX_SYS_CONTROL1_AMPINSEL (0x3<<0) +#define TFA98XX_SYS_CONTROL1_AMPINSEL_POS 0 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_LEN 2 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_MAX 3 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_MSK 0x3 + +/* + * src_set_configured + */ +#define TFA98XX_SYS_CONTROL1_MANSCONF (0x1<<2) +#define TFA98XX_SYS_CONTROL1_MANSCONF_POS 2 +#define TFA98XX_SYS_CONTROL1_MANSCONF_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANSCONF_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANSCONF_MSK 0x4 + +/* + * execute_cold_start + */ +#define TFA98XX_SYS_CONTROL1_MANCOLD (0x1<<3) +#define TFA98XX_SYS_CONTROL1_MANCOLD_POS 3 +#define TFA98XX_SYS_CONTROL1_MANCOLD_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANCOLD_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANCOLD_MSK 0x8 + +/* + * enbl_osc1m_auto_off + */ +#define TFA98XX_SYS_CONTROL1_MANAOOSC (0x1<<4) +#define TFA98XX_SYS_CONTROL1_MANAOOSC_POS 4 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_MSK 0x10 + +/* + * man_enbl_brown_out + */ +#define TFA98XX_SYS_CONTROL1_MANROBOD (0x1<<5) +#define TFA98XX_SYS_CONTROL1_MANROBOD_POS 5 +#define TFA98XX_SYS_CONTROL1_MANROBOD_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANROBOD_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANROBOD_MSK 0x20 + +/* + * enbl_bod + */ +#define TFA98XX_SYS_CONTROL1_BODE (0x1<<6) +#define TFA98XX_SYS_CONTROL1_BODE_POS 6 +#define TFA98XX_SYS_CONTROL1_BODE_LEN 1 +#define TFA98XX_SYS_CONTROL1_BODE_MAX 1 +#define TFA98XX_SYS_CONTROL1_BODE_MSK 0x40 + +/* + * enbl_bod_hyst + */ +#define TFA98XX_SYS_CONTROL1_BODHYS (0x1<<7) +#define TFA98XX_SYS_CONTROL1_BODHYS_POS 7 +#define TFA98XX_SYS_CONTROL1_BODHYS_LEN 1 +#define TFA98XX_SYS_CONTROL1_BODHYS_MAX 1 +#define TFA98XX_SYS_CONTROL1_BODHYS_MSK 0x80 + +/* + * bod_delay + */ +#define TFA98XX_SYS_CONTROL1_BODFILT (0x3<<8) +#define TFA98XX_SYS_CONTROL1_BODFILT_POS 8 +#define TFA98XX_SYS_CONTROL1_BODFILT_LEN 2 +#define TFA98XX_SYS_CONTROL1_BODFILT_MAX 3 +#define TFA98XX_SYS_CONTROL1_BODFILT_MSK 0x300 + +/* + * bod_lvlsel + */ +#define TFA98XX_SYS_CONTROL1_BODTHLVL (0x3<<10) +#define TFA98XX_SYS_CONTROL1_BODTHLVL_POS 10 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_LEN 2 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_MAX 3 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_MSK 0xc00 + +/* + * disable_mute_time_out + */ +#define TFA98XX_SYS_CONTROL1_MUTETO (0x1<<13) +#define TFA98XX_SYS_CONTROL1_MUTETO_POS 13 +#define TFA98XX_SYS_CONTROL1_MUTETO_LEN 1 +#define TFA98XX_SYS_CONTROL1_MUTETO_MAX 1 +#define TFA98XX_SYS_CONTROL1_MUTETO_MSK 0x2000 + +/* + * pwm_sel_rcv_ns + */ +#define TFA98XX_SYS_CONTROL1_RCVNS (0x1<<14) +#define TFA98XX_SYS_CONTROL1_RCVNS_POS 14 +#define TFA98XX_SYS_CONTROL1_RCVNS_LEN 1 +#define TFA98XX_SYS_CONTROL1_RCVNS_MAX 1 +#define TFA98XX_SYS_CONTROL1_RCVNS_MSK 0x4000 + +/* + * man_enbl_watchdog + */ +#define TFA98XX_SYS_CONTROL1_MANWDE (0x1<<15) +#define TFA98XX_SYS_CONTROL1_MANWDE_POS 15 +#define TFA98XX_SYS_CONTROL1_MANWDE_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANWDE_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANWDE_MSK 0x8000 + + +/* + * (0x02)-sys_control2 + */ + +/* + * audio_fs + */ +#define TFA98XX_SYS_CONTROL2_AUDFS (0xf<<0) +#define TFA98XX_SYS_CONTROL2_AUDFS_POS 0 +#define TFA98XX_SYS_CONTROL2_AUDFS_LEN 4 +#define TFA98XX_SYS_CONTROL2_AUDFS_MAX 15 +#define TFA98XX_SYS_CONTROL2_AUDFS_MSK 0xf + +/* + * input_level + */ +#define TFA98XX_SYS_CONTROL2_INPLEV (0x1<<4) +#define TFA98XX_SYS_CONTROL2_INPLEV_POS 4 +#define TFA98XX_SYS_CONTROL2_INPLEV_LEN 1 +#define TFA98XX_SYS_CONTROL2_INPLEV_MAX 1 +#define TFA98XX_SYS_CONTROL2_INPLEV_MSK 0x10 + +/* + * cs_frac_delay + */ +#define TFA98XX_SYS_CONTROL2_FRACTDEL (0x3f<<5) +#define TFA98XX_SYS_CONTROL2_FRACTDEL_POS 5 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_LEN 6 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_MAX 63 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_MSK 0x7e0 + +/* + * bypass_hvbat_filter + */ +#define TFA98XX_SYS_CONTROL2_BYPHVBF (0x1<<11) +#define TFA98XX_SYS_CONTROL2_BYPHVBF_POS 11 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_LEN 1 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_MAX 1 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_MSK 0x800 + +/* + * ctrl_rcvldop_bypass + */ +#define TFA98XX_SYS_CONTROL2_LDOBYP (0x1<<12) +#define TFA98XX_SYS_CONTROL2_LDOBYP_POS 12 +#define TFA98XX_SYS_CONTROL2_LDOBYP_LEN 1 +#define TFA98XX_SYS_CONTROL2_LDOBYP_MAX 1 +#define TFA98XX_SYS_CONTROL2_LDOBYP_MSK 0x1000 + + +/* + * (0x03)-device_revision + */ + +/* + * device_rev + */ +#define TFA98XX_DEVICE_REVISION_REV (0xffff<<0) +#define TFA98XX_DEVICE_REVISION_REV_POS 0 +#define TFA98XX_DEVICE_REVISION_REV_LEN 16 +#define TFA98XX_DEVICE_REVISION_REV_MAX 65535 +#define TFA98XX_DEVICE_REVISION_REV_MSK 0xffff + + +/* + * (0x04)-clock_control + */ + +/* + * pll_clkin_sel + */ +#define TFA98XX_CLOCK_CONTROL_REFCKEXT (0x3<<0) +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_POS 0 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_LEN 2 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_MAX 3 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_MSK 0x3 + +/* + * pll_clkin_sel_osc + */ +#define TFA98XX_CLOCK_CONTROL_REFCKSEL (0x1<<2) +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_POS 2 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_LEN 1 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_MAX 1 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_MSK 0x4 + + +/* + * (0x05)-clock_gating_control + */ + +/* + * enbl_spkr_ss_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE (0x1<<0) +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_POS 0 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_MSK 0x1 + +/* + * enbl_spkr_ss_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE (0x1<<1) +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_POS 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_MSK 0x2 + +/* + * enbl_volsense_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE (0x1<<2) +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_POS 2 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_MSK 0x4 + +/* + * enbl_volsense_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE (0x1<<3) +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_POS 3 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_MSK 0x8 + +/* + * enbl_cursense_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE (0x1<<4) +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_POS 4 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_MSK 0x10 + +/* + * enbl_cursense_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE (0x1<<5) +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_POS 5 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_MSK 0x20 + +/* + * enbl_pdm_ss + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME (0x1<<6) +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_POS 6 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_MSK 0x40 + + +/* + * (0x0d)-side_tone_config + */ + +/* + * side_tone_gain + */ +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN (0x1ff<<1) +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_POS 1 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_LEN 9 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_MAX 511 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_MSK 0x3fe + +/* + * mute_side_tone + */ +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE (0x1<<10) +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_POS 10 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_LEN 1 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_MAX 1 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_MSK 0x400 + + +/* + * (0x0e)-ctrl_digtoana_reg + */ + +/* + * ctrl_digtoana + */ +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP (0x7f<<0) +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_POS 0 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_LEN 7 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_MAX 127 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_MSK 0x7f + + +/* + * (0x10)-status_flags0 + */ + +/* + * flag_por + */ +#define TFA98XX_STATUS_FLAGS0_VDDS (0x1<<0) +#define TFA98XX_STATUS_FLAGS0_VDDS_POS 0 +#define TFA98XX_STATUS_FLAGS0_VDDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_VDDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_VDDS_MSK 0x1 + +/* + * flag_pll_lock + */ +#define TFA98XX_STATUS_FLAGS0_PLLS (0x1<<1) +#define TFA98XX_STATUS_FLAGS0_PLLS_POS 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_MSK 0x2 + +/* + * flag_otpok + */ +#define TFA98XX_STATUS_FLAGS0_OTDS (0x1<<2) +#define TFA98XX_STATUS_FLAGS0_OTDS_POS 2 +#define TFA98XX_STATUS_FLAGS0_OTDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_OTDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_OTDS_MSK 0x4 + +/* + * flag_ovpok + */ +#define TFA98XX_STATUS_FLAGS0_OVDS (0x1<<3) +#define TFA98XX_STATUS_FLAGS0_OVDS_POS 3 +#define TFA98XX_STATUS_FLAGS0_OVDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_OVDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_OVDS_MSK 0x8 + +/* + * flag_uvpok + */ +#define TFA98XX_STATUS_FLAGS0_UVDS (0x1<<4) +#define TFA98XX_STATUS_FLAGS0_UVDS_POS 4 +#define TFA98XX_STATUS_FLAGS0_UVDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_UVDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_UVDS_MSK 0x10 + +/* + * flag_clocks_stable + */ +#define TFA98XX_STATUS_FLAGS0_CLKS (0x1<<5) +#define TFA98XX_STATUS_FLAGS0_CLKS_POS 5 +#define TFA98XX_STATUS_FLAGS0_CLKS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_CLKS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_CLKS_MSK 0x20 + +/* + * flag_mtp_busy + */ +#define TFA98XX_STATUS_FLAGS0_MTPB (0x1<<6) +#define TFA98XX_STATUS_FLAGS0_MTPB_POS 6 +#define TFA98XX_STATUS_FLAGS0_MTPB_LEN 1 +#define TFA98XX_STATUS_FLAGS0_MTPB_MAX 1 +#define TFA98XX_STATUS_FLAGS0_MTPB_MSK 0x40 + +/* + * flag_lost_clk + */ +#define TFA98XX_STATUS_FLAGS0_NOCLK (0x1<<7) +#define TFA98XX_STATUS_FLAGS0_NOCLK_POS 7 +#define TFA98XX_STATUS_FLAGS0_NOCLK_LEN 1 +#define TFA98XX_STATUS_FLAGS0_NOCLK_MAX 1 +#define TFA98XX_STATUS_FLAGS0_NOCLK_MSK 0x80 + +/* + * flag_cf_speakererror + */ +#define TFA98XX_STATUS_FLAGS0_SPKS (0x1<<8) +#define TFA98XX_STATUS_FLAGS0_SPKS_POS 8 +#define TFA98XX_STATUS_FLAGS0_SPKS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_SPKS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_SPKS_MSK 0x100 + +/* + * flag_cold_started + */ +#define TFA98XX_STATUS_FLAGS0_ACS (0x1<<9) +#define TFA98XX_STATUS_FLAGS0_ACS_POS 9 +#define TFA98XX_STATUS_FLAGS0_ACS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_ACS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_ACS_MSK 0x200 + +/* + * flag_engage + */ +#define TFA98XX_STATUS_FLAGS0_SWS (0x1<<10) +#define TFA98XX_STATUS_FLAGS0_SWS_POS 10 +#define TFA98XX_STATUS_FLAGS0_SWS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_SWS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_SWS_MSK 0x400 + +/* + * flag_watchdog_reset + */ +#define TFA98XX_STATUS_FLAGS0_WDS (0x1<<11) +#define TFA98XX_STATUS_FLAGS0_WDS_POS 11 +#define TFA98XX_STATUS_FLAGS0_WDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_WDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_WDS_MSK 0x800 + +/* + * flag_enbl_amp + */ +#define TFA98XX_STATUS_FLAGS0_AMPS (0x1<<12) +#define TFA98XX_STATUS_FLAGS0_AMPS_POS 12 +#define TFA98XX_STATUS_FLAGS0_AMPS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_AMPS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_AMPS_MSK 0x1000 + +/* + * flag_enbl_ref + */ +#define TFA98XX_STATUS_FLAGS0_AREFS (0x1<<13) +#define TFA98XX_STATUS_FLAGS0_AREFS_POS 13 +#define TFA98XX_STATUS_FLAGS0_AREFS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_AREFS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_AREFS_MSK 0x2000 + +/* + * flag_adc10_ready + */ +#define TFA98XX_STATUS_FLAGS0_ADCCR (0x1<<14) +#define TFA98XX_STATUS_FLAGS0_ADCCR_POS 14 +#define TFA98XX_STATUS_FLAGS0_ADCCR_LEN 1 +#define TFA98XX_STATUS_FLAGS0_ADCCR_MAX 1 +#define TFA98XX_STATUS_FLAGS0_ADCCR_MSK 0x4000 + +/* + * flag_bod_vddd_nok + */ +#define TFA98XX_STATUS_FLAGS0_BODNOK (0x1<<15) +#define TFA98XX_STATUS_FLAGS0_BODNOK_POS 15 +#define TFA98XX_STATUS_FLAGS0_BODNOK_LEN 1 +#define TFA98XX_STATUS_FLAGS0_BODNOK_MAX 1 +#define TFA98XX_STATUS_FLAGS0_BODNOK_MSK 0x8000 + + +/* + * (0x11)-status_flags1 + */ + +/* + * flag_bst_bstcur + */ +#define TFA98XX_STATUS_FLAGS1_DCIL (0x1<<0) +#define TFA98XX_STATUS_FLAGS1_DCIL_POS 0 +#define TFA98XX_STATUS_FLAGS1_DCIL_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCIL_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCIL_MSK 0x1 + +/* + * flag_bst_hiz + */ +#define TFA98XX_STATUS_FLAGS1_DCDCA (0x1<<1) +#define TFA98XX_STATUS_FLAGS1_DCDCA_POS 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_MSK 0x2 + +/* + * flag_bst_ocpok + */ +#define TFA98XX_STATUS_FLAGS1_DCOCPOK (0x1<<2) +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_POS 2 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_MSK 0x4 + +/* + * flag_bst_voutcomp + */ +#define TFA98XX_STATUS_FLAGS1_DCHVBAT (0x1<<4) +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_POS 4 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_MSK 0x10 + +/* + * flag_bst_voutcomp86 + */ +#define TFA98XX_STATUS_FLAGS1_DCH114 (0x1<<5) +#define TFA98XX_STATUS_FLAGS1_DCH114_POS 5 +#define TFA98XX_STATUS_FLAGS1_DCH114_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCH114_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCH114_MSK 0x20 + +/* + * flag_bst_voutcomp93 + */ +#define TFA98XX_STATUS_FLAGS1_DCH107 (0x1<<6) +#define TFA98XX_STATUS_FLAGS1_DCH107_POS 6 +#define TFA98XX_STATUS_FLAGS1_DCH107_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCH107_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCH107_MSK 0x40 + +/* + * flag_soft_mute_busy + */ +#define TFA98XX_STATUS_FLAGS1_STMUTEB (0x1<<7) +#define TFA98XX_STATUS_FLAGS1_STMUTEB_POS 7 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_LEN 1 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_MAX 1 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_MSK 0x80 + +/* + * flag_soft_mute_state + */ +#define TFA98XX_STATUS_FLAGS1_STMUTE (0x1<<8) +#define TFA98XX_STATUS_FLAGS1_STMUTE_POS 8 +#define TFA98XX_STATUS_FLAGS1_STMUTE_LEN 1 +#define TFA98XX_STATUS_FLAGS1_STMUTE_MAX 1 +#define TFA98XX_STATUS_FLAGS1_STMUTE_MSK 0x100 + +/* + * flag_tdm_lut_error + */ +#define TFA98XX_STATUS_FLAGS1_TDMLUTER (0x1<<9) +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_POS 9 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_LEN 1 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_MAX 1 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_MSK 0x200 + +/* + * flag_tdm_status + */ +#define TFA98XX_STATUS_FLAGS1_TDMSTAT (0x7<<10) +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_POS 10 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_LEN 3 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_MAX 7 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_MSK 0x1c00 + +/* + * flag_tdm_error + */ +#define TFA98XX_STATUS_FLAGS1_TDMERR (0x1<<13) +#define TFA98XX_STATUS_FLAGS1_TDMERR_POS 13 +#define TFA98XX_STATUS_FLAGS1_TDMERR_LEN 1 +#define TFA98XX_STATUS_FLAGS1_TDMERR_MAX 1 +#define TFA98XX_STATUS_FLAGS1_TDMERR_MSK 0x2000 + +/* + * flag_haptic_busy + */ +#define TFA98XX_STATUS_FLAGS1_HAPTIC (0x1<<14) +#define TFA98XX_STATUS_FLAGS1_HAPTIC_POS 14 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_LEN 1 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_MAX 1 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_MSK 0x4000 + + +/* + * (0x12)-status_flags2 + */ + +/* + * flag_ocpokap_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOAPL (0x1<<0) +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_POS 0 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_MSK 0x1 + +/* + * flag_ocpokan_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOANL (0x1<<1) +#define TFA98XX_STATUS_FLAGS2_OCPOANL_POS 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_MSK 0x2 + +/* + * flag_ocpokbp_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBPL (0x1<<2) +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_POS 2 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_MSK 0x4 + +/* + * flag_ocpokbn_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBNL (0x1<<3) +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_POS 3 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_MSK 0x8 + +/* + * flag_clipa_high_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPAHL (0x1<<4) +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_POS 4 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_MSK 0x10 + +/* + * flag_clipa_low_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPALL (0x1<<5) +#define TFA98XX_STATUS_FLAGS2_CLIPALL_POS 5 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_MSK 0x20 + +/* + * flag_clipb_high_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPBHL (0x1<<6) +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_POS 6 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_MSK 0x40 + +/* + * flag_clipb_low_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPBLL (0x1<<7) +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_POS 7 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_MSK 0x80 + +/* + * flag_ocpokap_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC (0x1<<8) +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_POS 8 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_MSK 0x100 + +/* + * flag_ocpokan_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOANRC (0x1<<9) +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_POS 9 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_MSK 0x200 + +/* + * flag_ocpokbp_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC (0x1<<10) +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_POS 10 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_MSK 0x400 + +/* + * flag_ocpokbn_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC (0x1<<11) +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_POS 11 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_MSK 0x800 + +/* + * flag_rcvldop_ready + */ +#define TFA98XX_STATUS_FLAGS2_RCVLDOR (0x1<<12) +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_POS 12 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_LEN 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_MAX 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_MSK 0x1000 + +/* + * flag_rcvldop_bypassready + */ +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR (0x1<<13) +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_POS 13 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_LEN 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_MAX 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_MSK 0x2000 + +/* + * flag_ocp_alarm_left + */ +#define TFA98XX_STATUS_FLAGS2_OCDSL (0x1<<14) +#define TFA98XX_STATUS_FLAGS2_OCDSL_POS 14 +#define TFA98XX_STATUS_FLAGS2_OCDSL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCDSL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCDSL_MSK 0x4000 + +/* + * flag_clip_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPSL (0x1<<15) +#define TFA98XX_STATUS_FLAGS2_CLIPSL_POS 15 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_MSK 0x8000 + + +/* + * (0x13)-status_flags3 + */ + +/* + * flag_ocpokap_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOAPR (0x1<<0) +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_POS 0 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_MSK 0x1 + +/* + * flag_ocpokan_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOANR (0x1<<1) +#define TFA98XX_STATUS_FLAGS3_OCPOANR_POS 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_MSK 0x2 + +/* + * flag_ocpokbp_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOBPR (0x1<<2) +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_POS 2 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_MSK 0x4 + +/* + * flag_ocpokbn_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOBNR (0x1<<3) +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_POS 3 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_MSK 0x8 + +/* + * flag_clipa_high_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPAHR (0x1<<4) +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_POS 4 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_MSK 0x10 + +/* + * flag_clipa_low_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPALR (0x1<<5) +#define TFA98XX_STATUS_FLAGS3_CLIPALR_POS 5 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_MSK 0x20 + +/* + * flag_clipb_high_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPBHR (0x1<<6) +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_POS 6 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_MSK 0x40 + +/* + * flag_clipb_low_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPBLR (0x1<<7) +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_POS 7 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_MSK 0x80 + +/* + * flag_ocp_alarm_right + */ +#define TFA98XX_STATUS_FLAGS3_OCDSR (0x1<<8) +#define TFA98XX_STATUS_FLAGS3_OCDSR_POS 8 +#define TFA98XX_STATUS_FLAGS3_OCDSR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCDSR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCDSR_MSK 0x100 + +/* + * flag_clip_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPSR (0x1<<9) +#define TFA98XX_STATUS_FLAGS3_CLIPSR_POS 9 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_MSK 0x200 + +/* + * flag_mic_ocpok + */ +#define TFA98XX_STATUS_FLAGS3_OCPOKMC (0x1<<10) +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_POS 10 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_MSK 0x400 + +/* + * flag_man_alarm_state + */ +#define TFA98XX_STATUS_FLAGS3_MANALARM (0x1<<11) +#define TFA98XX_STATUS_FLAGS3_MANALARM_POS 11 +#define TFA98XX_STATUS_FLAGS3_MANALARM_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANALARM_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANALARM_MSK 0x800 + +/* + * flag_man_wait_src_settings + */ +#define TFA98XX_STATUS_FLAGS3_MANWAIT1 (0x1<<12) +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_POS 12 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_MSK 0x1000 + +/* + * flag_man_wait_cf_config + */ +#define TFA98XX_STATUS_FLAGS3_MANWAIT2 (0x1<<13) +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_POS 13 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_MSK 0x2000 + +/* + * flag_man_start_mute_audio + */ +#define TFA98XX_STATUS_FLAGS3_MANMUTE (0x1<<14) +#define TFA98XX_STATUS_FLAGS3_MANMUTE_POS 14 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_MSK 0x4000 + +/* + * flag_man_operating_state + */ +#define TFA98XX_STATUS_FLAGS3_MANOPER (0x1<<15) +#define TFA98XX_STATUS_FLAGS3_MANOPER_POS 15 +#define TFA98XX_STATUS_FLAGS3_MANOPER_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANOPER_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANOPER_MSK 0x8000 + + +/* + * (0x14)-status_flags4 + */ + +/* + * flag_cf_speakererror_left + */ +#define TFA98XX_STATUS_FLAGS4_SPKSL (0x1<<0) +#define TFA98XX_STATUS_FLAGS4_SPKSL_POS 0 +#define TFA98XX_STATUS_FLAGS4_SPKSL_LEN 1 +#define TFA98XX_STATUS_FLAGS4_SPKSL_MAX 1 +#define TFA98XX_STATUS_FLAGS4_SPKSL_MSK 0x1 + +/* + * flag_cf_speakererror_right + */ +#define TFA98XX_STATUS_FLAGS4_SPKSR (0x1<<1) +#define TFA98XX_STATUS_FLAGS4_SPKSR_POS 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_LEN 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_MAX 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_MSK 0x2 + +/* + * flag_clk_out_of_range + */ +#define TFA98XX_STATUS_FLAGS4_CLKOOR (0x1<<2) +#define TFA98XX_STATUS_FLAGS4_CLKOOR_POS 2 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_LEN 1 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_MAX 1 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_MSK 0x4 + +/* + * man_state + */ +#define TFA98XX_STATUS_FLAGS4_MANSTATE (0xf<<3) +#define TFA98XX_STATUS_FLAGS4_MANSTATE_POS 3 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_LEN 4 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_MAX 15 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_MSK 0x78 + + +/* + * (0x15)-battery_voltage + */ + +/* + * bat_adc + */ +#define TFA98XX_BATTERY_VOLTAGE_BATS (0x3ff<<0) +#define TFA98XX_BATTERY_VOLTAGE_BATS_POS 0 +#define TFA98XX_BATTERY_VOLTAGE_BATS_LEN 10 +#define TFA98XX_BATTERY_VOLTAGE_BATS_MAX 1023 +#define TFA98XX_BATTERY_VOLTAGE_BATS_MSK 0x3ff + + +/* + * (0x16)-temperature + */ + +/* + * temp_adc + */ +#define TFA98XX_TEMPERATURE_TEMPS (0x1ff<<0) +#define TFA98XX_TEMPERATURE_TEMPS_POS 0 +#define TFA98XX_TEMPERATURE_TEMPS_LEN 9 +#define TFA98XX_TEMPERATURE_TEMPS_MAX 511 +#define TFA98XX_TEMPERATURE_TEMPS_MSK 0x1ff + + +/* + * (0x20)-tdm_config0 + */ + +/* + * tdm_usecase + */ +#define TFA98XX_TDM_CONFIG0_TDMUC (0xf<<0) +#define TFA98XX_TDM_CONFIG0_TDMUC_POS 0 +#define TFA98XX_TDM_CONFIG0_TDMUC_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMUC_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMUC_MSK 0xf + +/* + * tdm_enable + */ +#define TFA98XX_TDM_CONFIG0_TDME (0x1<<4) +#define TFA98XX_TDM_CONFIG0_TDME_POS 4 +#define TFA98XX_TDM_CONFIG0_TDME_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDME_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDME_MSK 0x10 + +/* + * tdm_mode + */ +#define TFA98XX_TDM_CONFIG0_TDMMODE (0x1<<5) +#define TFA98XX_TDM_CONFIG0_TDMMODE_POS 5 +#define TFA98XX_TDM_CONFIG0_TDMMODE_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMMODE_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMMODE_MSK 0x20 + +/* + * tdm_clk_inversion + */ +#define TFA98XX_TDM_CONFIG0_TDMCLINV (0x1<<6) +#define TFA98XX_TDM_CONFIG0_TDMCLINV_POS 6 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_MSK 0x40 + +/* + * tdm_fs_ws_length + */ +#define TFA98XX_TDM_CONFIG0_TDMFSLN (0xf<<7) +#define TFA98XX_TDM_CONFIG0_TDMFSLN_POS 7 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_MSK 0x780 + +/* + * tdm_fs_ws_polarity + */ +#define TFA98XX_TDM_CONFIG0_TDMFSPOL (0x1<<11) +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_POS 11 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_MSK 0x800 + +/* + * tdm_nbck + */ +#define TFA98XX_TDM_CONFIG0_TDMNBCK (0xf<<12) +#define TFA98XX_TDM_CONFIG0_TDMNBCK_POS 12 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_MSK 0xf000 + + +/* + * (0x21)-tdm_config1 + */ + +/* + * tdm_nb_of_slots + */ +#define TFA98XX_TDM_CONFIG1_TDMSLOTS (0xf<<0) +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_POS 0 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_LEN 4 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_MAX 15 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_MSK 0xf + +/* + * tdm_slot_length + */ +#define TFA98XX_TDM_CONFIG1_TDMSLLN (0x1f<<4) +#define TFA98XX_TDM_CONFIG1_TDMSLLN_POS 4 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_LEN 5 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_MAX 31 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_MSK 0x1f0 + +/* + * tdm_bits_remaining + */ +#define TFA98XX_TDM_CONFIG1_TDMBRMG (0x1f<<9) +#define TFA98XX_TDM_CONFIG1_TDMBRMG_POS 9 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_LEN 5 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_MAX 31 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_MSK 0x3e00 + +/* + * tdm_data_delay + */ +#define TFA98XX_TDM_CONFIG1_TDMDEL (0x1<<14) +#define TFA98XX_TDM_CONFIG1_TDMDEL_POS 14 +#define TFA98XX_TDM_CONFIG1_TDMDEL_LEN 1 +#define TFA98XX_TDM_CONFIG1_TDMDEL_MAX 1 +#define TFA98XX_TDM_CONFIG1_TDMDEL_MSK 0x4000 + +/* + * tdm_data_adjustment + */ +#define TFA98XX_TDM_CONFIG1_TDMADJ (0x1<<15) +#define TFA98XX_TDM_CONFIG1_TDMADJ_POS 15 +#define TFA98XX_TDM_CONFIG1_TDMADJ_LEN 1 +#define TFA98XX_TDM_CONFIG1_TDMADJ_MAX 1 +#define TFA98XX_TDM_CONFIG1_TDMADJ_MSK 0x8000 + + +/* + * (0x22)-tdm_config2 + */ + +/* + * tdm_audio_sample_compression + */ +#define TFA98XX_TDM_CONFIG2_TDMOOMP (0x3<<0) +#define TFA98XX_TDM_CONFIG2_TDMOOMP_POS 0 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_MSK 0x3 + +/* + * tdm_sample_size + */ +#define TFA98XX_TDM_CONFIG2_TDMSSIZE (0x1f<<2) +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_POS 2 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_LEN 5 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_MAX 31 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_MSK 0x7c + +/* + * tdm_txdata_format + */ +#define TFA98XX_TDM_CONFIG2_TDMTXDFO (0x3<<7) +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_POS 7 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_MSK 0x180 + +/* + * tdm_txdata_format_unused_slot_sd0 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS0 (0x3<<9) +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_POS 9 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_MSK 0x600 + +/* + * tdm_txdata_format_unused_slot_sd1 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS1 (0x3<<11) +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_POS 11 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_MSK 0x1800 + +/* + * tdm_txdata_format_unused_slot_sd2 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS2 (0x3<<13) +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_POS 13 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_MSK 0x6000 + + +/* + * (0x23)-tdm_config3 + */ + +/* + * tdm_sink1_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMLE (0x1<<1) +#define TFA98XX_TDM_CONFIG3_TDMLE_POS 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_MSK 0x2 + +/* + * tdm_sink2_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMRE (0x1<<2) +#define TFA98XX_TDM_CONFIG3_TDMRE_POS 2 +#define TFA98XX_TDM_CONFIG3_TDMRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMRE_MSK 0x4 + +/* + * tdm_source1_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMVSRE (0x1<<4) +#define TFA98XX_TDM_CONFIG3_TDMVSRE_POS 4 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_MSK 0x10 + +/* + * tdm_source2_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCSRE (0x1<<5) +#define TFA98XX_TDM_CONFIG3_TDMCSRE_POS 5 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_MSK 0x20 + +/* + * tdm_source3_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMVSLE (0x1<<6) +#define TFA98XX_TDM_CONFIG3_TDMVSLE_POS 6 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_MSK 0x40 + +/* + * tdm_source4_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCSLE (0x1<<7) +#define TFA98XX_TDM_CONFIG3_TDMCSLE_POS 7 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_MSK 0x80 + +/* + * tdm_source5_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCFRE (0x1<<8) +#define TFA98XX_TDM_CONFIG3_TDMCFRE_POS 8 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_MSK 0x100 + +/* + * tdm_source6_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCFLE (0x1<<9) +#define TFA98XX_TDM_CONFIG3_TDMCFLE_POS 9 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_MSK 0x200 + +/* + * tdm_source7_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCF3E (0x1<<10) +#define TFA98XX_TDM_CONFIG3_TDMCF3E_POS 10 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_MSK 0x400 + +/* + * tdm_source8_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCF4E (0x1<<11) +#define TFA98XX_TDM_CONFIG3_TDMCF4E_POS 11 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_MSK 0x800 + +/* + * tdm_source9_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMPD1E (0x1<<12) +#define TFA98XX_TDM_CONFIG3_TDMPD1E_POS 12 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_MSK 0x1000 + +/* + * tdm_source10_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMPD2E (0x1<<13) +#define TFA98XX_TDM_CONFIG3_TDMPD2E_POS 13 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_MSK 0x2000 + + +/* + * (0x24)-tdm_config4 + */ + +/* + * tdm_sink1_io + */ +#define TFA98XX_TDM_CONFIG4_TDMLIO (0x3<<2) +#define TFA98XX_TDM_CONFIG4_TDMLIO_POS 2 +#define TFA98XX_TDM_CONFIG4_TDMLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMLIO_MSK 0xc + +/* + * tdm_sink2_io + */ +#define TFA98XX_TDM_CONFIG4_TDMRIO (0x3<<4) +#define TFA98XX_TDM_CONFIG4_TDMRIO_POS 4 +#define TFA98XX_TDM_CONFIG4_TDMRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMRIO_MSK 0x30 + +/* + * tdm_source1_io + */ +#define TFA98XX_TDM_CONFIG4_TDMVSRIO (0x3<<8) +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_POS 8 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_MSK 0x300 + +/* + * tdm_source2_io + */ +#define TFA98XX_TDM_CONFIG4_TDMCSRIO (0x3<<10) +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_POS 10 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_MSK 0xc00 + +/* + * tdm_source3_io + */ +#define TFA98XX_TDM_CONFIG4_TDMVSLIO (0x3<<12) +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_POS 12 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_MSK 0x3000 + +/* + * tdm_source4_io + */ +#define TFA98XX_TDM_CONFIG4_TDMCSLIO (0x3<<14) +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_POS 14 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_MSK 0xc000 + + +/* + * (0x25)-tdm_config5 + */ + +/* + * tdm_source5_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCFRIO (0x3<<0) +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_POS 0 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_MSK 0x3 + +/* + * tdm_source6_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCFLIO (0x3<<2) +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_POS 2 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_MSK 0xc + +/* + * tdm_source7_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCF3IO (0x3<<4) +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_POS 4 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_MSK 0x30 + +/* + * tdm_source8_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCF4IO (0x3<<6) +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_POS 6 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_MSK 0xc0 + +/* + * tdm_source9_io + */ +#define TFA98XX_TDM_CONFIG5_TDMPD1IO (0x3<<8) +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_POS 8 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_MSK 0x300 + +/* + * tdm_source10_io + */ +#define TFA98XX_TDM_CONFIG5_TDMPD2IO (0x3<<10) +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_POS 10 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_MSK 0xc00 + + +/* + * (0x26)-tdm_config6 + */ + +/* + * tdm_sink1_slot + */ +#define TFA98XX_TDM_CONFIG6_TDMLS (0xf<<4) +#define TFA98XX_TDM_CONFIG6_TDMLS_POS 4 +#define TFA98XX_TDM_CONFIG6_TDMLS_LEN 4 +#define TFA98XX_TDM_CONFIG6_TDMLS_MAX 15 +#define TFA98XX_TDM_CONFIG6_TDMLS_MSK 0xf0 + +/* + * tdm_sink2_slot + */ +#define TFA98XX_TDM_CONFIG6_TDMRS (0xf<<8) +#define TFA98XX_TDM_CONFIG6_TDMRS_POS 8 +#define TFA98XX_TDM_CONFIG6_TDMRS_LEN 4 +#define TFA98XX_TDM_CONFIG6_TDMRS_MAX 15 +#define TFA98XX_TDM_CONFIG6_TDMRS_MSK 0xf00 + + +/* + * (0x27)-tdm_config7 + */ + +/* + * tdm_source1_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMVSRS (0xf<<0) +#define TFA98XX_TDM_CONFIG7_TDMVSRS_POS 0 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_MSK 0xf + +/* + * tdm_source2_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMCSRS (0xf<<4) +#define TFA98XX_TDM_CONFIG7_TDMCSRS_POS 4 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_MSK 0xf0 + +/* + * tdm_source3_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMVSLS (0xf<<8) +#define TFA98XX_TDM_CONFIG7_TDMVSLS_POS 8 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_MSK 0xf00 + +/* + * tdm_source4_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMCSLS (0xf<<12) +#define TFA98XX_TDM_CONFIG7_TDMCSLS_POS 12 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_MSK 0xf000 + + +/* + * (0x28)-tdm_config8 + */ + +/* + * tdm_source5_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCFRS (0xf<<0) +#define TFA98XX_TDM_CONFIG8_TDMCFRS_POS 0 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_MSK 0xf + +/* + * tdm_source6_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCFLS (0xf<<4) +#define TFA98XX_TDM_CONFIG8_TDMCFLS_POS 4 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_MSK 0xf0 + +/* + * tdm_source7_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCF3S (0xf<<8) +#define TFA98XX_TDM_CONFIG8_TDMCF3S_POS 8 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_MSK 0xf00 + +/* + * tdm_source8_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCF4S (0xf<<12) +#define TFA98XX_TDM_CONFIG8_TDMCF4S_POS 12 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_MSK 0xf000 + + +/* + * (0x29)-tdm_config9 + */ + +/* + * tdm_source9_slot + */ +#define TFA98XX_TDM_CONFIG9_TDMPD1S (0xf<<0) +#define TFA98XX_TDM_CONFIG9_TDMPD1S_POS 0 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_LEN 4 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_MAX 15 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_MSK 0xf + +/* + * tdm_source10_slot + */ +#define TFA98XX_TDM_CONFIG9_TDMPD2S (0xf<<4) +#define TFA98XX_TDM_CONFIG9_TDMPD2S_POS 4 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_LEN 4 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_MAX 15 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_MSK 0xf0 + + +/* + * (0x31)-pdm_config0 + */ + +/* + * pdm_mode + */ +#define TFA98XX_PDM_CONFIG0_PDMSM (0x1<<0) +#define TFA98XX_PDM_CONFIG0_PDMSM_POS 0 +#define TFA98XX_PDM_CONFIG0_PDMSM_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMSM_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMSM_MSK 0x1 + +/* + * pdm_side_tone_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMSTSEL (0x3<<1) +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_POS 1 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_LEN 2 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_MAX 3 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_MSK 0x6 + +/* + * pdm_left_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMLSEL (0x1<<3) +#define TFA98XX_PDM_CONFIG0_PDMLSEL_POS 3 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_MSK 0x8 + +/* + * pdm_right_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMRSEL (0x1<<4) +#define TFA98XX_PDM_CONFIG0_PDMRSEL_POS 4 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_MSK 0x10 + +/* + * enbl_micvdd + */ +#define TFA98XX_PDM_CONFIG0_MICVDDE (0x1<<5) +#define TFA98XX_PDM_CONFIG0_MICVDDE_POS 5 +#define TFA98XX_PDM_CONFIG0_MICVDDE_LEN 1 +#define TFA98XX_PDM_CONFIG0_MICVDDE_MAX 1 +#define TFA98XX_PDM_CONFIG0_MICVDDE_MSK 0x20 + + +/* + * (0x32)-pdm_config1 + */ + +/* + * pdm_nbck + */ +#define TFA98XX_PDM_CONFIG1_PDMCLRAT (0x3<<0) +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_POS 0 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_LEN 2 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_MAX 3 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_MSK 0x3 + +/* + * pdm_gain + */ +#define TFA98XX_PDM_CONFIG1_PDMGAIN (0xf<<2) +#define TFA98XX_PDM_CONFIG1_PDMGAIN_POS 2 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_LEN 4 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_MAX 15 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_MSK 0x3c + +/* + * sel_pdm_out_data + */ +#define TFA98XX_PDM_CONFIG1_PDMOSEL (0xf<<6) +#define TFA98XX_PDM_CONFIG1_PDMOSEL_POS 6 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_LEN 4 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_MAX 15 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_MSK 0x3c0 + +/* + * sel_cf_haptic_data + */ +#define TFA98XX_PDM_CONFIG1_SELCFHAPD (0x1<<10) +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_POS 10 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_LEN 1 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_MAX 1 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_MSK 0x400 + + +/* + * (0x33)-haptic_driver_config + */ + +/* + * haptic_duration + */ +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME (0xff<<0) +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_POS 0 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_LEN 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_MAX 255 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_MSK 0xff + +/* + * haptic_data + */ +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL (0xff<<8) +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_POS 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_LEN 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_MAX 255 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_MSK 0xff00 + + +/* + * (0x34)-gpio_datain_reg + */ + +/* + * gpio_datain + */ +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN (0xf<<0) +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_POS 0 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_LEN 4 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_MAX 15 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_MSK 0xf + + +/* + * (0x35)-gpio_config + */ + +/* + * gpio_ctrl + */ +#define TFA98XX_GPIO_CONFIG_GPIOCTRL (0x1<<0) +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_POS 0 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_LEN 1 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_MAX 1 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_MSK 0x1 + +/* + * gpio_dir + */ +#define TFA98XX_GPIO_CONFIG_GPIOCONF (0xf<<1) +#define TFA98XX_GPIO_CONFIG_GPIOCONF_POS 1 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_LEN 4 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_MAX 15 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_MSK 0x1e + +/* + * gpio_dataout + */ +#define TFA98XX_GPIO_CONFIG_GPIODOUT (0xf<<5) +#define TFA98XX_GPIO_CONFIG_GPIODOUT_POS 5 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_LEN 4 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_MAX 15 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_MSK 0x1e0 + + +/* + * (0x40)-interrupt_out_reg1 + */ + +/* + * int_out_flag_por + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_MSK 0x1 + +/* + * int_out_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_MSK 0x2 + +/* + * int_out_flag_otpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_MSK 0x4 + +/* + * int_out_flag_ovpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_MSK 0x8 + +/* + * int_out_flag_uvpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_POS 4 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_MSK 0x10 + +/* + * int_out_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS (0x1<<5) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_POS 5 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_MSK 0x20 + +/* + * int_out_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_POS 6 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_MSK 0x40 + +/* + * int_out_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_POS 7 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_MSK 0x80 + +/* + * int_out_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS (0x1<<8) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_POS 8 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_MSK 0x100 + +/* + * int_out_flag_cold_started + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS (0x1<<9) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_POS 9 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_MSK 0x200 + +/* + * int_out_flag_engage + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS (0x1<<10) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_POS 10 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_MSK 0x400 + +/* + * int_out_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS (0x1<<11) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_POS 11 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_MSK 0x800 + +/* + * int_out_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_POS 12 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_MSK 0x1000 + +/* + * int_out_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_POS 13 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_MSK 0x2000 + +/* + * int_out_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_POS 14 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_MSK 0x4000 + +/* + * int_out_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_POS 15 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_MSK 0x8000 + + +/* + * (0x41)-interrupt_out_reg2 + */ + +/* + * int_out_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_MSK 0x1 + +/* + * int_out_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_MSK 0x2 + +/* + * int_out_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_MSK 0x4 + +/* + * int_out_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_MSK 0x8 + +/* + * int_out_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_POS 4 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_MSK 0x10 + +/* + * int_out_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_POS 5 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_MSK 0x20 + +/* + * int_out_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_POS 6 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_MSK 0x40 + +/* + * int_out_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_POS 7 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_MSK 0x80 + +/* + * int_out_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_POS 8 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_MSK 0x100 + +/* + * int_out_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_POS 9 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_MSK 0x200 + +/* + * int_out_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_POS 10 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_MSK 0x400 + +/* + * int_out_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_POS 11 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_MSK 0x800 + +/* + * int_out_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_POS 12 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_MSK 0x1000 + +/* + * int_out_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER (0x1<<13) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_POS 13 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_MSK 0x2000 + +/* + * int_out_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_POS 14 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_MSK 0x4000 + +/* + * int_out_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_POS 15 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_MSK 0x8000 + + +/* + * (0x42)-interrupt_out_reg3 + */ + +/* + * int_out_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_MSK 0x1 + +/* + * int_out_flag_clip_left + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_MSK 0x2 + +/* + * int_out_flag_clip_right + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_MSK 0x4 + +/* + * int_out_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_MSK 0x8 + + +/* + * (0x44)-interrupt_in_reg1 + */ + +/* + * int_in_flag_por + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_POS 0 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_MSK 0x1 + +/* + * int_in_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_POS 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_MSK 0x2 + +/* + * int_in_flag_otpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_POS 2 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_MSK 0x4 + +/* + * int_in_flag_ovpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_POS 3 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_MSK 0x8 + +/* + * int_in_flag_uvpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_POS 4 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_MSK 0x10 + +/* + * int_in_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS (0x1<<5) +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_POS 5 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_MSK 0x20 + +/* + * int_in_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_POS 6 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_MSK 0x40 + +/* + * int_in_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_POS 7 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_MSK 0x80 + +/* + * int_in_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS (0x1<<8) +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_POS 8 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_MSK 0x100 + +/* + * int_in_flag_cold_started + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS (0x1<<9) +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_POS 9 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_MSK 0x200 + +/* + * int_in_flag_engage + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS (0x1<<10) +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_POS 10 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_MSK 0x400 + +/* + * int_in_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS (0x1<<11) +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_POS 11 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_MSK 0x800 + +/* + * int_in_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_POS 12 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_MSK 0x1000 + +/* + * int_in_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_POS 13 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_MSK 0x2000 + +/* + * int_in_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_POS 14 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_MSK 0x4000 + +/* + * int_in_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_POS 15 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_MSK 0x8000 + + +/* + * (0x45)-interrupt_in_reg2 + */ + +/* + * int_in_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_POS 0 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_MSK 0x1 + +/* + * int_in_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_POS 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_MSK 0x2 + +/* + * int_in_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_POS 2 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_MSK 0x4 + +/* + * int_in_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_POS 3 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_MSK 0x8 + +/* + * int_in_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_POS 4 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_MSK 0x10 + +/* + * int_in_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_POS 5 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_MSK 0x20 + +/* + * int_in_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_POS 6 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_MSK 0x40 + +/* + * int_in_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_POS 7 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_MSK 0x80 + +/* + * int_in_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_POS 8 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_MSK 0x100 + +/* + * int_in_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_POS 9 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_MSK 0x200 + +/* + * int_in_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_POS 10 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_MSK 0x400 + +/* + * int_in_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_POS 11 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_MSK 0x800 + +/* + * int_in_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_POS 12 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_MSK 0x1000 + +/* + * int_in_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER (0x1<<13) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_POS 13 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_MSK 0x2000 + +/* + * int_in_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_POS 14 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_MSK 0x4000 + +/* + * int_in_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_POS 15 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_MSK 0x8000 + + +/* + * (0x46)-interrupt_in_reg3 + */ + +/* + * int_in_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_POS 0 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_MSK 0x1 + +/* + * int_in_flag_clip_left + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_POS 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_MSK 0x2 + +/* + * int_in_flag_clip_right + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_POS 2 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_MSK 0x4 + +/* + * int_in_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_POS 3 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_MSK 0x8 + + +/* + * (0x48)-interrupt_enable_reg1 + */ + +/* + * int_enable_flag_por + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_MSK 0x1 + +/* + * int_enable_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_MSK 0x2 + +/* + * int_enable_flag_otpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_MSK 0x4 + +/* + * int_enable_flag_ovpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_MSK 0x8 + +/* + * int_enable_flag_uvpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_POS 4 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_MSK 0x10 + +/* + * int_enable_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS (0x1<<5) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_POS 5 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_MSK 0x20 + +/* + * int_enable_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_POS 6 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_MSK 0x40 + +/* + * int_enable_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_POS 7 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_MSK 0x80 + +/* + * int_enable_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS (0x1<<8) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_POS 8 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_MSK 0x100 + +/* + * int_enable_flag_cold_started + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS (0x1<<9) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_POS 9 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_MSK 0x200 + +/* + * int_enable_flag_engage + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS (0x1<<10) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_POS 10 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_MSK 0x400 + +/* + * int_enable_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS (0x1<<11) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_POS 11 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_MSK 0x800 + +/* + * int_enable_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_POS 12 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_MSK 0x1000 + +/* + * int_enable_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_POS 13 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_MSK 0x2000 + +/* + * int_enable_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_POS 14 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_MSK 0x4000 + +/* + * int_enable_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_POS 15 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_MSK 0x8000 + + +/* + * (0x49)-interrupt_enable_reg2 + */ + +/* + * int_enable_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_MSK 0x1 + +/* + * int_enable_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_MSK 0x2 + +/* + * int_enable_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_MSK 0x4 + +/* + * int_enable_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_MSK 0x8 + +/* + * int_enable_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_POS 4 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_MSK 0x10 + +/* + * int_enable_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_POS 5 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_MSK 0x20 + +/* + * int_enable_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_POS 6 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_MSK 0x40 + +/* + * int_enable_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_POS 7 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_MSK 0x80 + +/* + * int_enable_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_POS 8 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_MSK 0x100 + +/* + * int_enable_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_POS 9 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_MSK 0x200 + +/* + * int_enable_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_POS 10 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_MSK 0x400 + +/* + * int_enable_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_POS 11 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_MSK 0x800 + +/* + * int_enable_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_POS 12 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_MSK 0x1000 + +/* + * int_enable_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER (0x1<<13) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_POS 13 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_MSK 0x2000 + +/* + * int_enable_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_POS 14 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_MSK 0x4000 + +/* + * int_enable_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_POS 15 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_MSK 0x8000 + + +/* + * (0x4a)-interrupt_enable_reg3 + */ + +/* + * int_enable_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_MSK 0x1 + +/* + * int_enable_flag_clip_left + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_MSK 0x2 + +/* + * int_enable_flag_clip_right + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_MSK 0x4 + +/* + * int_enable_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1 (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_MSK 0x8 + + +/* + * (0x4c)-status_polarity_reg1 + */ + +/* + * int_polarity_flag_por + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_POS 0 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_MSK 0x1 + +/* + * int_polarity_flag_pll_lock + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_POS 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_MSK 0x2 + +/* + * int_polarity_flag_otpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_POS 2 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_MSK 0x4 + +/* + * int_polarity_flag_ovpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_POS 3 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_MSK 0x8 + +/* + * int_polarity_flag_uvpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS (0x1<<4) +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_POS 4 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_MSK 0x10 + +/* + * int_polarity_flag_clocks_stable + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS (0x1<<5) +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_POS 5 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_MSK 0x20 + +/* + * int_polarity_flag_mtp_busy + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB (0x1<<6) +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_POS 6 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_MSK 0x40 + +/* + * int_polarity_flag_lost_clk + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK (0x1<<7) +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_POS 7 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_MSK 0x80 + +/* + * int_polarity_flag_cf_speakererror + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS (0x1<<8) +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_POS 8 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_MSK 0x100 + +/* + * int_polarity_flag_cold_started + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS (0x1<<9) +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_POS 9 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_MSK 0x200 + +/* + * int_polarity_flag_engage + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS (0x1<<10) +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_POS 10 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_MSK 0x400 + +/* + * int_polarity_flag_watchdog_reset + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS (0x1<<11) +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_POS 11 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_MSK 0x800 + +/* + * int_polarity_flag_enbl_amp + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS (0x1<<12) +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_POS 12 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_MSK 0x1000 + +/* + * int_polarity_flag_enbl_ref + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS (0x1<<13) +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_POS 13 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_MSK 0x2000 + +/* + * int_polarity_flag_adc10_ready + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR (0x1<<14) +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_POS 14 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_MSK 0x4000 + +/* + * int_polarity_flag_bod_vddd_nok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK (0x1<<15) +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_POS 15 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_MSK 0x8000 + + +/* + * (0x4d)-status_polarity_reg2 + */ + +/* + * int_polarity_flag_bst_bstcur + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_POS 0 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_MSK 0x1 + +/* + * int_polarity_flag_bst_hiz + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_POS 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_MSK 0x2 + +/* + * int_polarity_flag_bst_ocpok + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_POS 2 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_MSK 0x4 + +/* + * int_polarity_flag_bst_peakcur + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_POS 3 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_MSK 0x8 + +/* + * int_polarity_flag_bst_voutcomp + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC (0x1<<4) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_POS 4 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_MSK 0x10 + +/* + * int_polarity_flag_bst_voutcomp86 + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86 (0x1<<5) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_POS 5 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_MSK 0x20 + +/* + * int_polarity_flag_bst_voutcomp93 + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93 (0x1<<6) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_POS 6 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_MSK 0x40 + +/* + * int_polarity_flag_rcvldop_ready + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD (0x1<<7) +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_POS 7 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_MSK 0x80 + +/* + * int_polarity_flag_ocp_alarm_left + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL (0x1<<8) +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_POS 8 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_MSK 0x100 + +/* + * int_polarity_flag_ocp_alarm_right + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR (0x1<<9) +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_POS 9 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_MSK 0x200 + +/* + * int_polarity_flag_man_wait_src_settings + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC (0x1<<10) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_POS 10 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_MSK 0x400 + +/* + * int_polarity_flag_man_wait_cf_config + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC (0x1<<11) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_POS 11 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_MSK 0x800 + +/* + * int_polarity_flag_man_start_mute_audio + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU (0x1<<12) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_POS 12 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_MSK 0x1000 + +/* + * int_polarity_flag_cfma_err + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER (0x1<<13) +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_POS 13 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_MSK 0x2000 + +/* + * int_polarity_flag_cfma_ack + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC (0x1<<14) +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_POS 14 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_MSK 0x4000 + +/* + * int_polarity_flag_clk_out_of_range + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR (0x1<<15) +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_POS 15 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_MSK 0x8000 + + +/* + * (0x4e)-status_polarity_reg3 + */ + +/* + * int_polarity_flag_tdm_error + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_POS 0 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_MSK 0x1 + +/* + * int_polarity_flag_clip_left + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_POS 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_MSK 0x2 + +/* + * int_polarity_flag_clip_right + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_POS 2 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_MSK 0x4 + +/* + * int_polarity_flag_mic_ocpok + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_POS 3 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_MSK 0x8 + + +/* + * (0x50)-bat_prot_config + */ + +/* + * vbat_prot_attack_time + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSCR (0x3<<0) +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_POS 0 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_MSK 0x3 + +/* + * vbat_prot_thlevel + */ +#define TFA98XX_BAT_PROT_CONFIG_BSST (0xf<<2) +#define TFA98XX_BAT_PROT_CONFIG_BSST_POS 2 +#define TFA98XX_BAT_PROT_CONFIG_BSST_LEN 4 +#define TFA98XX_BAT_PROT_CONFIG_BSST_MAX 15 +#define TFA98XX_BAT_PROT_CONFIG_BSST_MSK 0x3c + +/* + * vbat_prot_max_reduct + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSRL (0x3<<6) +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_POS 6 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_MSK 0xc0 + +/* + * vbat_prot_release_time + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSRR (0x7<<8) +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_POS 8 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_LEN 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_MAX 7 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_MSK 0x700 + +/* + * vbat_prot_hysterese + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSHY (0x3<<11) +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_POS 11 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_MSK 0x1800 + +/* + * sel_vbat + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSR (0x1<<14) +#define TFA98XX_BAT_PROT_CONFIG_BSSR_POS 14 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_LEN 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_MAX 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_MSK 0x4000 + +/* + * bypass_clipper + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSBY (0x1<<15) +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_POS 15 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_LEN 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_MAX 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_MSK 0x8000 + + +/* + * (0x51)-audio_control + */ + +/* + * batsense_steepness + */ +#define TFA98XX_AUDIO_CONTROL_BSSS (0x1<<0) +#define TFA98XX_AUDIO_CONTROL_BSSS_POS 0 +#define TFA98XX_AUDIO_CONTROL_BSSS_LEN 1 +#define TFA98XX_AUDIO_CONTROL_BSSS_MAX 1 +#define TFA98XX_AUDIO_CONTROL_BSSS_MSK 0x1 + +/* + * soft_mute + */ +#define TFA98XX_AUDIO_CONTROL_INTSMUTE (0x1<<1) +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_POS 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_LEN 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_MAX 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_MSK 0x2 + +/* + * cf_mute_left + */ +#define TFA98XX_AUDIO_CONTROL_CFSML (0x1<<2) +#define TFA98XX_AUDIO_CONTROL_CFSML_POS 2 +#define TFA98XX_AUDIO_CONTROL_CFSML_LEN 1 +#define TFA98XX_AUDIO_CONTROL_CFSML_MAX 1 +#define TFA98XX_AUDIO_CONTROL_CFSML_MSK 0x4 + +/* + * cf_mute_right + */ +#define TFA98XX_AUDIO_CONTROL_CFSMR (0x1<<3) +#define TFA98XX_AUDIO_CONTROL_CFSMR_POS 3 +#define TFA98XX_AUDIO_CONTROL_CFSMR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_CFSMR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_CFSMR_MSK 0x8 + +/* + * bypass_hp_left + */ +#define TFA98XX_AUDIO_CONTROL_HPFBYPL (0x1<<4) +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_POS 4 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_LEN 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_MAX 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_MSK 0x10 + +/* + * bypass_hp_right + */ +#define TFA98XX_AUDIO_CONTROL_HPFBYPR (0x1<<5) +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_POS 5 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_MSK 0x20 + +/* + * enbl_dpsa_left + */ +#define TFA98XX_AUDIO_CONTROL_DPSAL (0x1<<6) +#define TFA98XX_AUDIO_CONTROL_DPSAL_POS 6 +#define TFA98XX_AUDIO_CONTROL_DPSAL_LEN 1 +#define TFA98XX_AUDIO_CONTROL_DPSAL_MAX 1 +#define TFA98XX_AUDIO_CONTROL_DPSAL_MSK 0x40 + +/* + * enbl_dpsa_right + */ +#define TFA98XX_AUDIO_CONTROL_DPSAR (0x1<<7) +#define TFA98XX_AUDIO_CONTROL_DPSAR_POS 7 +#define TFA98XX_AUDIO_CONTROL_DPSAR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_DPSAR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_DPSAR_MSK 0x80 + +/* + * cf_volume + */ +#define TFA98XX_AUDIO_CONTROL_VOL (0xff<<8) +#define TFA98XX_AUDIO_CONTROL_VOL_POS 8 +#define TFA98XX_AUDIO_CONTROL_VOL_LEN 8 +#define TFA98XX_AUDIO_CONTROL_VOL_MAX 255 +#define TFA98XX_AUDIO_CONTROL_VOL_MSK 0xff00 + + +/* + * (0x52)-amplifier_config + */ + +/* + * ctrl_rcv + */ +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV (0x1<<0) +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_POS 0 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_LEN 1 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_MAX 1 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_MSK 0x1 + +/* + * ctrl_cc + */ +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL (0x7<<2) +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_POS 2 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_LEN 3 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_MAX 7 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_MSK 0x1c + +/* + * gain + */ +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN (0xff<<5) +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_POS 5 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_LEN 8 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_MAX 255 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_MSK 0x1fe0 + +/* + * ctrl_slopectrl + */ +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE (0x1<<13) +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_POS 13 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_LEN 1 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_MAX 1 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_MSK 0x2000 + +/* + * ctrl_slope + */ +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET (0x3<<14) +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_POS 14 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_LEN 2 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_MAX 3 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_MSK 0xc000 + + +/* + * (0x5a)-audio_control2 + */ + +/* + * cf_volume_sec + */ +#define TFA98XX_AUDIO_CONTROL2_VOLSEC (0xff<<0) +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_POS 0 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_LEN 8 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_MAX 255 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_MSK 0xff + +/* + * sw_profile + */ +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL (0xff<<8) +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_POS 8 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_LEN 8 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_MAX 255 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_MSK 0xff00 + + +/* + * (0x70)-dcdc_control0 + */ + +/* + * boost_volt + */ +#define TFA98XX_DCDC_CONTROL0_DCVO (0x7<<0) +#define TFA98XX_DCDC_CONTROL0_DCVO_POS 0 +#define TFA98XX_DCDC_CONTROL0_DCVO_LEN 3 +#define TFA98XX_DCDC_CONTROL0_DCVO_MAX 7 +#define TFA98XX_DCDC_CONTROL0_DCVO_MSK 0x7 + +/* + * boost_cur + */ +#define TFA98XX_DCDC_CONTROL0_DCMCC (0xf<<3) +#define TFA98XX_DCDC_CONTROL0_DCMCC_POS 3 +#define TFA98XX_DCDC_CONTROL0_DCMCC_LEN 4 +#define TFA98XX_DCDC_CONTROL0_DCMCC_MAX 15 +#define TFA98XX_DCDC_CONTROL0_DCMCC_MSK 0x78 + +/* + * bst_coil_value + */ +#define TFA98XX_DCDC_CONTROL0_DCCV (0x3<<7) +#define TFA98XX_DCDC_CONTROL0_DCCV_POS 7 +#define TFA98XX_DCDC_CONTROL0_DCCV_LEN 2 +#define TFA98XX_DCDC_CONTROL0_DCCV_MAX 3 +#define TFA98XX_DCDC_CONTROL0_DCCV_MSK 0x180 + +/* + * boost_intel + */ +#define TFA98XX_DCDC_CONTROL0_DCIE (0x1<<9) +#define TFA98XX_DCDC_CONTROL0_DCIE_POS 9 +#define TFA98XX_DCDC_CONTROL0_DCIE_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCIE_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCIE_MSK 0x200 + +/* + * boost_speed + */ +#define TFA98XX_DCDC_CONTROL0_DCSR (0x1<<10) +#define TFA98XX_DCDC_CONTROL0_DCSR_POS 10 +#define TFA98XX_DCDC_CONTROL0_DCSR_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCSR_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCSR_MSK 0x400 + +/* + * dcdc_synchronisation + */ +#define TFA98XX_DCDC_CONTROL0_DCSYNCP (0x7<<11) +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_POS 11 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_LEN 3 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_MAX 7 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_MSK 0x3800 + +/* + * dcdcoff_mode + */ +#define TFA98XX_DCDC_CONTROL0_DCDIS (0x1<<14) +#define TFA98XX_DCDC_CONTROL0_DCDIS_POS 14 +#define TFA98XX_DCDC_CONTROL0_DCDIS_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCDIS_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCDIS_MSK 0x4000 + + +/* + * (0x90)-cf_controls + */ + +/* + * cf_rst_dsp + */ +#define TFA98XX_CF_CONTROLS_RST (0x1<<0) +#define TFA98XX_CF_CONTROLS_RST_POS 0 +#define TFA98XX_CF_CONTROLS_RST_LEN 1 +#define TFA98XX_CF_CONTROLS_RST_MAX 1 +#define TFA98XX_CF_CONTROLS_RST_MSK 0x1 + +/* + * cf_dmem + */ +#define TFA98XX_CF_CONTROLS_DMEM (0x3<<1) +#define TFA98XX_CF_CONTROLS_DMEM_POS 1 +#define TFA98XX_CF_CONTROLS_DMEM_LEN 2 +#define TFA98XX_CF_CONTROLS_DMEM_MAX 3 +#define TFA98XX_CF_CONTROLS_DMEM_MSK 0x6 + +/* + * cf_aif + */ +#define TFA98XX_CF_CONTROLS_AIF (0x1<<3) +#define TFA98XX_CF_CONTROLS_AIF_POS 3 +#define TFA98XX_CF_CONTROLS_AIF_LEN 1 +#define TFA98XX_CF_CONTROLS_AIF_MAX 1 +#define TFA98XX_CF_CONTROLS_AIF_MSK 0x8 + +/* + * cf_int + */ +#define TFA98XX_CF_CONTROLS_CFINT (0x1<<4) +#define TFA98XX_CF_CONTROLS_CFINT_POS 4 +#define TFA98XX_CF_CONTROLS_CFINT_LEN 1 +#define TFA98XX_CF_CONTROLS_CFINT_MAX 1 +#define TFA98XX_CF_CONTROLS_CFINT_MSK 0x10 + +/* + * cf_cgate_off + */ +#define TFA98XX_CF_CONTROLS_CFCGATE (0x1<<5) +#define TFA98XX_CF_CONTROLS_CFCGATE_POS 5 +#define TFA98XX_CF_CONTROLS_CFCGATE_LEN 1 +#define TFA98XX_CF_CONTROLS_CFCGATE_MAX 1 +#define TFA98XX_CF_CONTROLS_CFCGATE_MSK 0x20 + +/* + * cf_req_cmd + */ +#define TFA98XX_CF_CONTROLS_REQCMD (0x1<<8) +#define TFA98XX_CF_CONTROLS_REQCMD_POS 8 +#define TFA98XX_CF_CONTROLS_REQCMD_LEN 1 +#define TFA98XX_CF_CONTROLS_REQCMD_MAX 1 +#define TFA98XX_CF_CONTROLS_REQCMD_MSK 0x100 + +/* + * cf_req_reset + */ +#define TFA98XX_CF_CONTROLS_REQRST (0x1<<9) +#define TFA98XX_CF_CONTROLS_REQRST_POS 9 +#define TFA98XX_CF_CONTROLS_REQRST_LEN 1 +#define TFA98XX_CF_CONTROLS_REQRST_MAX 1 +#define TFA98XX_CF_CONTROLS_REQRST_MSK 0x200 + +/* + * cf_req_mips + */ +#define TFA98XX_CF_CONTROLS_REQMIPS (0x1<<10) +#define TFA98XX_CF_CONTROLS_REQMIPS_POS 10 +#define TFA98XX_CF_CONTROLS_REQMIPS_LEN 1 +#define TFA98XX_CF_CONTROLS_REQMIPS_MAX 1 +#define TFA98XX_CF_CONTROLS_REQMIPS_MSK 0x400 + +/* + * cf_req_mute_ready + */ +#define TFA98XX_CF_CONTROLS_REQMUTED (0x1<<11) +#define TFA98XX_CF_CONTROLS_REQMUTED_POS 11 +#define TFA98XX_CF_CONTROLS_REQMUTED_LEN 1 +#define TFA98XX_CF_CONTROLS_REQMUTED_MAX 1 +#define TFA98XX_CF_CONTROLS_REQMUTED_MSK 0x800 + +/* + * cf_req_volume_ready + */ +#define TFA98XX_CF_CONTROLS_REQVOL (0x1<<12) +#define TFA98XX_CF_CONTROLS_REQVOL_POS 12 +#define TFA98XX_CF_CONTROLS_REQVOL_LEN 1 +#define TFA98XX_CF_CONTROLS_REQVOL_MAX 1 +#define TFA98XX_CF_CONTROLS_REQVOL_MSK 0x1000 + +/* + * cf_req_damage + */ +#define TFA98XX_CF_CONTROLS_REQDMG (0x1<<13) +#define TFA98XX_CF_CONTROLS_REQDMG_POS 13 +#define TFA98XX_CF_CONTROLS_REQDMG_LEN 1 +#define TFA98XX_CF_CONTROLS_REQDMG_MAX 1 +#define TFA98XX_CF_CONTROLS_REQDMG_MSK 0x2000 + +/* + * cf_req_calibrate_ready + */ +#define TFA98XX_CF_CONTROLS_REQCAL (0x1<<14) +#define TFA98XX_CF_CONTROLS_REQCAL_POS 14 +#define TFA98XX_CF_CONTROLS_REQCAL_LEN 1 +#define TFA98XX_CF_CONTROLS_REQCAL_MAX 1 +#define TFA98XX_CF_CONTROLS_REQCAL_MSK 0x4000 + +/* + * cf_req_reserved + */ +#define TFA98XX_CF_CONTROLS_REQRSV (0x1<<15) +#define TFA98XX_CF_CONTROLS_REQRSV_POS 15 +#define TFA98XX_CF_CONTROLS_REQRSV_LEN 1 +#define TFA98XX_CF_CONTROLS_REQRSV_MAX 1 +#define TFA98XX_CF_CONTROLS_REQRSV_MSK 0x8000 + + +/* + * (0x91)-cf_mad + */ + +/* + * cf_madd + */ +#define TFA98XX_CF_MAD_MADD (0xffff<<0) +#define TFA98XX_CF_MAD_MADD_POS 0 +#define TFA98XX_CF_MAD_MADD_LEN 16 +#define TFA98XX_CF_MAD_MADD_MAX 65535 +#define TFA98XX_CF_MAD_MADD_MSK 0xffff + + +/* + * (0x92)-cf_mem + */ + +/* + * cf_mema + */ +#define TFA98XX_CF_MEM_MEMA (0xffff<<0) +#define TFA98XX_CF_MEM_MEMA_POS 0 +#define TFA98XX_CF_MEM_MEMA_LEN 16 +#define TFA98XX_CF_MEM_MEMA_MAX 65535 +#define TFA98XX_CF_MEM_MEMA_MSK 0xffff + + +/* + * (0x93)-cf_status + */ + +/* + * cf_err + */ +#define TFA98XX_CF_STATUS_ERR (0xff<<0) +#define TFA98XX_CF_STATUS_ERR_POS 0 +#define TFA98XX_CF_STATUS_ERR_LEN 8 +#define TFA98XX_CF_STATUS_ERR_MAX 255 +#define TFA98XX_CF_STATUS_ERR_MSK 0xff + +/* + * cf_ack_cmd + */ +#define TFA98XX_CF_STATUS_ACKCMD (0x1<<8) +#define TFA98XX_CF_STATUS_ACKCMD_POS 8 +#define TFA98XX_CF_STATUS_ACKCMD_LEN 1 +#define TFA98XX_CF_STATUS_ACKCMD_MAX 1 +#define TFA98XX_CF_STATUS_ACKCMD_MSK 0x100 + +/* + * cf_ack_reset + */ +#define TFA98XX_CF_STATUS_ACKRST (0x1<<9) +#define TFA98XX_CF_STATUS_ACKRST_POS 9 +#define TFA98XX_CF_STATUS_ACKRST_LEN 1 +#define TFA98XX_CF_STATUS_ACKRST_MAX 1 +#define TFA98XX_CF_STATUS_ACKRST_MSK 0x200 + +/* + * cf_ack_mips + */ +#define TFA98XX_CF_STATUS_ACKMIPS (0x1<<10) +#define TFA98XX_CF_STATUS_ACKMIPS_POS 10 +#define TFA98XX_CF_STATUS_ACKMIPS_LEN 1 +#define TFA98XX_CF_STATUS_ACKMIPS_MAX 1 +#define TFA98XX_CF_STATUS_ACKMIPS_MSK 0x400 + +/* + * cf_ack_mute_ready + */ +#define TFA98XX_CF_STATUS_ACKMUTED (0x1<<11) +#define TFA98XX_CF_STATUS_ACKMUTED_POS 11 +#define TFA98XX_CF_STATUS_ACKMUTED_LEN 1 +#define TFA98XX_CF_STATUS_ACKMUTED_MAX 1 +#define TFA98XX_CF_STATUS_ACKMUTED_MSK 0x800 + +/* + * cf_ack_volume_ready + */ +#define TFA98XX_CF_STATUS_ACKVOL (0x1<<12) +#define TFA98XX_CF_STATUS_ACKVOL_POS 12 +#define TFA98XX_CF_STATUS_ACKVOL_LEN 1 +#define TFA98XX_CF_STATUS_ACKVOL_MAX 1 +#define TFA98XX_CF_STATUS_ACKVOL_MSK 0x1000 + +/* + * cf_ack_damage + */ +#define TFA98XX_CF_STATUS_ACKDMG (0x1<<13) +#define TFA98XX_CF_STATUS_ACKDMG_POS 13 +#define TFA98XX_CF_STATUS_ACKDMG_LEN 1 +#define TFA98XX_CF_STATUS_ACKDMG_MAX 1 +#define TFA98XX_CF_STATUS_ACKDMG_MSK 0x2000 + +/* + * cf_ack_calibrate_ready + */ +#define TFA98XX_CF_STATUS_ACKCAL (0x1<<14) +#define TFA98XX_CF_STATUS_ACKCAL_POS 14 +#define TFA98XX_CF_STATUS_ACKCAL_LEN 1 +#define TFA98XX_CF_STATUS_ACKCAL_MAX 1 +#define TFA98XX_CF_STATUS_ACKCAL_MSK 0x4000 + +/* + * cf_ack_reserved + */ +#define TFA98XX_CF_STATUS_ACKRSV (0x1<<15) +#define TFA98XX_CF_STATUS_ACKRSV_POS 15 +#define TFA98XX_CF_STATUS_ACKRSV_LEN 1 +#define TFA98XX_CF_STATUS_ACKRSV_MAX 1 +#define TFA98XX_CF_STATUS_ACKRSV_MSK 0x8000 + + +/* + * (0xa1)-mtpkey2_reg + */ + +/* + * mtpkey2 + */ +#define TFA98XX_MTPKEY2_REG_MTPK (0xff<<0) +#define TFA98XX_MTPKEY2_REG_MTPK_POS 0 +#define TFA98XX_MTPKEY2_REG_MTPK_LEN 8 +#define TFA98XX_MTPKEY2_REG_MTPK_MAX 255 +#define TFA98XX_MTPKEY2_REG_MTPK_MSK 0xff + + +/* + * (0xa2)-mtp_status + */ + +/* + * key01_locked + */ +#define TFA98XX_MTP_STATUS_KEY1LOCKED (0x1<<0) +#define TFA98XX_MTP_STATUS_KEY1LOCKED_POS 0 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_LEN 1 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_MAX 1 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_MSK 0x1 + +/* + * key02_locked + */ +#define TFA98XX_MTP_STATUS_KEY2LOCKED (0x1<<1) +#define TFA98XX_MTP_STATUS_KEY2LOCKED_POS 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_LEN 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_MAX 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_MSK 0x2 + + +/* + * (0xa3)-KEY_protected_mtp_control + */ + +/* + * auto_copy_iic_to_mtp + */ +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP (0x1<<6) +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_POS 6 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_LEN 1 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_MAX 1 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_MSK 0x40 + + +/* + * (0xa5)-mtp_data_out_msb + */ + +/* + * mtp_man_data_out_msb + */ +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB (0xffff<<0) +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_POS 0 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_LEN 16 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_MAX 65535 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_MSK 0xffff + + +/* + * (0xa6)-mtp_data_out_lsb + */ + +/* + * mtp_man_data_out_lsb + */ +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB (0xffff<<0) +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_POS 0 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_LEN 16 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_MAX 65535 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_MSK 0xffff + + +/* + * (0xb1)-temp_sensor_config + */ + +/* + * ext_temp + */ +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS (0x1ff<<0) +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_POS 0 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_LEN 9 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_MAX 511 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_MSK 0x1ff + +/* + * ext_temp_sel + */ +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS (0x1<<9) +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_POS 9 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_LEN 1 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_MAX 1 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_MSK 0x200 + + +/* + * (0xf0)-KEY2_protected_MTP0 + */ + +/* + * calibration_onetime + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC (0x1<<0) +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS 0 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK 0x1 + +/* + * calibr_ron_done + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX (0x1<<1) +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK 0x2 + +/* + * calibr_dcdc_api_calibrate + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI (0x1<<2) +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_POS 2 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_MSK 0x4 + +/* + * calibr_dcdc_delta_sign + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB (0x1<<3) +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_POS 3 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_MSK 0x8 + +/* + * calibr_dcdc_delta + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF (0x7<<4) +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_POS 4 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_LEN 3 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_MAX 7 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_MSK 0x70 + + +/* + * (0xf4)-KEY1_protected_MTP4 + */ + + +/* + * (0xf5)-KEY1_protected_MTP5 + */ + +#endif /* TFA98XX_GENREGS_H */ diff --git a/sound/soc/codecs/tfa9891/tfa98xx_parameters.h b/sound/soc/codecs/tfa9891/tfa98xx_parameters.h new file mode 100755 index 000000000000..5ab6d28bb7a7 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa98xx_parameters.h @@ -0,0 +1,754 @@ +/* + * tfa98xx_parameters.h + * + * Created on: Jul 22, 2013 + * Author: NLV02095 + */ + +#ifndef TFA98XXPARAMETERS_H_ +#define TFA98XXPARAMETERS_H_ + +//#include "config.h" +// workaround for Visual Studio: +// fatal error C1083: Cannot open include file: 'config.h': No such file or directory +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "tfa_service.h" + +#if (defined(WIN32) || defined(_X64)) +/* These warnings are disabled because it is only given by Windows and there is no easy fix */ +#pragma warning(disable:4200) +#pragma warning(disable:4214) +#endif + +/* + * profiles & volumesteps + * + */ +#define TFA_MAX_PROFILES (64) +#define TFA_MAX_VSTEPS (64) +#define TFA_MAX_VSTEP_MSG_MARKER (100) /* This marker is used to indicate if all msgs need to be written to the device */ +#define TFA_MAX_MSGS (10) + +// the pack pragma is required to make that the size in memory +// matches the actual variable lenghts +// This is to assure that the binary files can be transported between +// different platforms. +#pragma pack (push, 1) + +/* + * typedef for 24 bit value using 3 bytes + */ +typedef struct uint24 { + uint8_t b[3]; +} uint24_t; +/* + * the generic header + * all char types are in ASCII + */ +typedef struct nxpTfaHeader { + uint16_t id; + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint16_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data + char customer[8]; // “name of customer” + char application[8]; // “application name” + char type[8]; // “application type name” +} nxpTfaHeader_t; + +typedef enum nxpTfaSamplerate { + fs_8k, // 8kHz + fs_11k025, // 11.025kHz + fs_12k, // 12kHz + fs_16k, // 16kHz + fs_22k05, // 22.05kHz + fs_24k, // 24kHz + fs_32k, // 32kHz + fs_44k1, // 44.1kHz + fs_48k, // 48kHz + fs_96k // 96kHz +} nxpTfaSamplerate_t; + +/* + * coolflux direct memory access + */ +typedef struct nxpTfaDspMem { + uint8_t type; /* 0--3: p, x, y, iomem */ + uint16_t address; /* target address */ + uint8_t size; /* data size in words */ + int words[]; /* payload in signed 32bit integer (two's complement) */ +} nxpTfaDspMem_t; + +/* + * the biquad coefficients for the API together with index in filter + * the biquad_index is the actual index in the equalizer +1 + */ +#define BIQUAD_COEFF_SIZE 6 + +/* +* Output fixed point coeffs structure +*/ +typedef struct { + int a2; + int a1; + int b2; + int b1; + int b0; +}nxpTfaBiquad_t; + +typedef struct nxpTfaBiquadOld { + uint8_t bytes[BIQUAD_COEFF_SIZE*sizeof(uint24_t)]; +}nxpTfaBiquadOld_t; + +typedef struct nxpTfaBiquadFloat { + float headroom; + float b0; + float b1; + float b2; + float a1; + float a2; +} nxpTfaBiquadFloat_t; + +/* +* EQ filter definitions +*/ +typedef enum nxpTfaFilterType { + fFlat, //Vary only gain + fLowpass, //2nd order Butterworth low pass + fHighpass, //2nd order Butterworth high pass + fLowshelf, + fHighshelf, + fNotch, + fPeak, + fBandpass, + f1stLP, + f1stHP, + fElliptic +} nxpTfaFilterType_t; + +/* + * filter parameters for biquad (re-)calculation + */ +typedef struct nxpTfaFilter { + nxpTfaBiquadOld_t biquad; + uint8_t enabled; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float frequency; + float Q; + float gain; +} nxpTfaFilter_t ; //8 * float + int32 + byte == 37 + +/* + * biquad params for calculation +*/ + +#define TFA_BQ_EQ_INDEX 0 +#define TFA_BQ_ANTI_ALIAS_INDEX 10 +#define TFA_BQ_INTEGRATOR_INDEX 13 + +/* +* Loudspeaker Compensation filter definitions +*/ +typedef struct nxpTfaLsCompensationFilter { + nxpTfaBiquad_t biquad; + uint8_t lsCompOn; // Loudspeaker compensation on/off; when 'off', the DSP code doesn't apply the bwExt => bwExtOn GUI flag should be gray to avoid confusion + uint8_t bwExtOn; // Bandwidth extension on/off + float fRes; // [Hz] speaker resonance frequency + float Qt; // speaker resonance Q-factor + float fBwExt; // [Hz] Band width extension frequency + float samplingFreq;// [Hz] Sampling frequency +} nxpTfaLsCompensationFilter_t; + +/* +* Anti Aliasing Elliptic filter definitions +*/ +typedef struct nxpTfaAntiAliasFilter { + nxpTfaBiquad_t biquad; /**< Output results fixed point coeffs */ + uint8_t enabled; + float cutOffFreq; // cut off frequency + float samplingFreq; // sampling frequency + float rippleDb; // range: [0.1 3.0] + float rolloff; // range: [-1.0 1.0] +} nxpTfaAntiAliasFilter_t; + +/** +* Integrator filter input definitions +*/ +typedef struct nxpTfaIntegratorFilter { + nxpTfaBiquad_t biquad; /**< Output results fixed point coeffs */ + uint8_t type; /**< Butterworth filter type: high or low pass */ + float cutOffFreq; /**< cut off frequency in Hertz; range: [100.0 4000.0] */ + float samplingFreq; /**< sampling frequency in Hertz */ + float leakage; /**< leakage factor; range [0.0 1.0] */ +} nxpTfaIntegratorFilter_t; + + +typedef struct nxpTfaEqFilter { + nxpTfaBiquad_t biquad; + uint8_t enabled; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float cutOffFreq; // cut off frequency, // range: [100.0 4000.0] + float samplingFreq; // sampling frequency + float Q; // range: [0.5 5.0] + float gainDb; // range: [-10.0 10.0] +} nxpTfaEqFilter_t ; //8 * float + int32 + byte == 37 + +typedef struct nxpTfaContAntiAlias { + int8_t index; /**< index determines destination type; anti-alias, integrator,eq */ + uint8_t type; + float cutOffFreq; // cut off frequency + float samplingFreq; + float rippleDb; // integrator leakage + float rolloff; + uint8_t bytes[5*3]; // payload 5*24buts coeffs +}nxpTfaContAntiAlias_t; + +typedef struct nxpTfaContIntegrator { + int8_t index; /**< index determines destination type; anti-alias, integrator,eq */ + uint8_t type; + float cutOffFreq; // cut off frequency + float samplingFreq; + float leakage; // integrator leakage + float reserved; + uint8_t bytes[5*3]; // payload 5*24buts coeffs +}nxpTfaContIntegrator_t; +typedef struct nxpTfaContEq { + int8_t index; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float cutOffFreq; // cut off frequency, // range: [100.0 4000.0] + float samplingFreq; // sampling frequency + float Q; // range: [0.5 5.0] + float gainDb; // range: [-10.0 10.0] + uint8_t bytes[5*3]; // payload 5*24buts coeffs +} nxpTfaContEq_t ; //8 * float + int32 + byte == 37 + +typedef union nxpTfaContBiquad { + nxpTfaContEq_t eq; + nxpTfaContAntiAlias_t aa; + nxpTfaContIntegrator_t in; +}nxpTfaContBiquad_t; + +#define TFA_BQ_EQ_INDEX 0 +#define TFA_BQ_ANTI_ALIAS_INDEX 10 +#define TFA_BQ_INTEGRATOR_INDEX 13 + +#define TFA98XX_MAX_EQ 10 +typedef struct nxpTfaEqualizer { + nxpTfaFilter_t filter[TFA98XX_MAX_EQ];// note: API index counts from 1..10 +} nxpTfaEqualizer_t; + +/* + * files + */ +#define HDR(c1,c2) (c2<<8|c1) // little endian +typedef enum nxpTfaHeaderType { + paramsHdr = HDR('P','M'), /* containter file */ + volstepHdr = HDR('V','P'), + patchHdr = HDR('P','A'), + speakerHdr = HDR('S','P'), + presetHdr = HDR('P','R'), + configHdr = HDR('C','O'), + equalizerHdr = HDR('E','Q'), + drcHdr = HDR('D','R'), + msgHdr = HDR('M','G'), /* generic message */ + infoHdr = HDR('I','N') +} nxpTfaHeaderType_t; + +/* + * equalizer file + */ +#define NXPTFA_EQ_VERSION '1' +#define NXPTFA_EQ_SUBVERSION "00" +typedef struct nxpTfaEqualizerFile { + nxpTfaHeader_t hdr; + uint8_t samplerate; // ==enum samplerates, assure 8 bits + nxpTfaFilter_t filter[TFA98XX_MAX_EQ];// note: API index counts from 1..10 +} nxpTfaEqualizerFile_t; + +/* + * patch file + */ +#define NXPTFA_PA_VERSION '1' +#define NXPTFA_PA_SUBVERSION "00" +typedef struct nxpTfaPatchFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaPatch_t; + +/* + * generic message file + * - the payload of this file includes the opcode and is send straight to the DSP + */ +#define NXPTFA_MG_VERSION '3' +#define NXPTFA_MG_SUBVERSION "00" +typedef struct nxpTfaMsgFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaMsgFile_t; + +/* + * NOTE the tfa98xx API defines the enum Tfa98xx_config_type that defines + * the subtypes as decribes below. + * tfa98xx_dsp_config_parameter_type() can be used to get the + * supported type for the active device.. + */ +/* + * config file V1 sub 1 + */ +#define NXPTFA_CO_VERSION '1' +#define NXPTFA_CO3_VERSION '3' +#define NXPTFA_CO_SUBVERSION1 "01" +typedef struct nxpTfaConfigS1File { + nxpTfaHeader_t hdr; + uint8_t data[55*3]; +} nxpTfaConfigS1_t; +/* + * config file V1 sub 2 + */ +#define NXPTFA_CO_SUBVERSION2 "02" +typedef struct nxpTfaConfigS2File { + nxpTfaHeader_t hdr; + uint8_t data[67*3]; +} nxpTfaConfigS2_t; +/* + * config file V1 sub 3 + */ +#define NXPTFA_CO_SUBVERSION3 "03" +typedef struct nxpTfaConfigS3File { + nxpTfaHeader_t hdr; + uint8_t data[67*3]; +} nxpTfaConfigS3_t; + +/* + * config file V1.0 + */ +#define NXPTFA_CO_SUBVERSION "00" +typedef struct nxpTfaConfigFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaConfig_t; + +/* + * preset file + */ +#define NXPTFA_PR_VERSION '1' +#define NXPTFA_PR_SUBVERSION "00" + +typedef struct nxpTfaPresetFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaPreset_t; +/* + * drc file + */ +#define NXPTFA_DR_VERSION '1' +#define NXPTFA_DR_SUBVERSION "00" +typedef struct nxpTfaDrcFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaDrc_t; + +/* + * drc file + * for tfa 2 there is also a xml-version + */ +#define NXPTFA_DR3_VERSION '3' +#define NXPTFA_DR3_SUBVERSION "00" +typedef struct nxpTfaDrcFile2 { + nxpTfaHeader_t hdr; + uint8_t version[3]; + uint8_t data[]; +} nxpTfaDrc2_t; + + +/* + * volume step structures + */ +// VP01 +#define NXPTFA_VP1_VERSION '1' +#define NXPTFA_VP1_SUBVERSION "01" +typedef struct nxpTfaVolumeStep1 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; +} nxpTfaVolumeStep1_t; + +// VP02 +#define NXPTFA_VP2_VERSION '2' +#define NXPTFA_VP2_SUBVERSION "01" +typedef struct nxpTfaVolumeStep2 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; + nxpTfaFilter_t filter[TFA98XX_MAX_EQ];// note: API index counts from 1..10 +} nxpTfaVolumeStep2_t; + +// VP03 is obsolete + +// VP04 +/** obsolete -DRC is now a different file +#define NXPTFA_VP4_VERSION "4" +#define NXPTFA_VP4_SUBVERSION "01" +typedef struct nxpTfaVolumeStep4 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; + nxpTfaEqualizer_t eq; +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) + uint8_t drc[TFA98XX_DRC_LENGTH]; +#endif +} nxpTfaVolumeStep4_t; +**/ +/* + * volumestep file + */ +#define NXPTFA_VP_VERSION '1' +#define NXPTFA_VP_SUBVERSION "00" +typedef struct nxpTfaVolumeStepFile { + nxpTfaHeader_t hdr; + uint8_t vsteps; // can also be calulated from size+type + uint8_t samplerate; // ==enum samplerates, assure 8 bits + uint8_t payload; //start of variable length contents:N times volsteps +}nxpTfaVolumeStepFile_t; +/* + * volumestep2 file + */ +typedef struct nxpTfaVolumeStep2File { + nxpTfaHeader_t hdr; + uint8_t vsteps; // can also be calulated from size+type + uint8_t samplerate; // ==enum samplerates, assure 8 bits + nxpTfaVolumeStep2_t vstep[]; //start of variable length contents:N times volsteps +}nxpTfaVolumeStep2File_t; + +/* + * volumestepMax2 file + */ +typedef struct nxpTfaVolumeStepMax2File { + nxpTfaHeader_t hdr; + uint8_t version[3]; + uint8_t NrOfVsteps; + uint8_t vstepsBin[]; +}nxpTfaVolumeStepMax2File_t; + +/* + * volumestepMax2 file + * This volumestep should ONLY be used for the use of bin2hdr! + * This can only be used to find the messagetype of the vstep (without header) + */ +typedef struct nxpTfaVolumeStepMax2_1File { + uint8_t version[3]; + uint8_t NrOfVsteps; + uint8_t vstepsBin[]; +}nxpTfaVolumeStepMax2_1File_t; + +struct nxpTfaVolumeStepRegisterInfo { + uint8_t NrOfRegisters; + uint16_t registerInfo[]; +}; + +struct nxpTfaVolumeStepMessageInfo { + uint8_t NrOfMessages; + uint8_t MessageType; + uint24_t MessageLength; + uint8_t CmdId[3]; + uint8_t ParameterData[]; +}; +/**************************old v2 *************************************************/ + +/* + * subv 00 volumestep file + */ +typedef struct nxpTfaOldHeader { + uint16_t id; + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint16_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data +} nxpTfaOldHeader_t; + +typedef struct nxpOldTfaFilter { + double bq[5]; + int32_t type; + double frequency; + double Q; + double gain; + uint8_t enabled; +} nxpTfaOldFilter_t ; //8 * float + int32 + byte == 37 +typedef struct nxpTfaOldVolumeStep2 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; + nxpTfaOldFilter_t eq[10]; +} nxpTfaOldVolumeStep2_t; +typedef struct nxpTfaOldVolumeStepFile { + nxpTfaOldHeader_t hdr; + nxpTfaOldVolumeStep2_t step[]; +// uint8_t payload; //start of variable length contents:N times volsteps + +}nxpTfaOldVolumeStep2File_t; +/**************************end old v2 *************************************************/ + +/* + * speaker file header + */ +struct nxpTfaSpkHeader { + struct nxpTfaHeader hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint16_t ohm; +}; + + +/* + * speaker file + */ +#define NXPTFA_SP_VERSION '1' +#define NXPTFA_SP_SUBVERSION "00" +typedef struct nxpTfaSpeakerFile { + nxpTfaHeader_t hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint8_t ohm_primary; + uint8_t ohm_secondary; + uint8_t data[]; //payload TFA98XX_SPEAKERPARAMETER_LENGTH +} nxpTfaSpeakerFile_t; + +#if ( defined( TFA9888 ) || defined( TFA98XX_FULL )) + +#define NXPTFA_VP3_VERSION '3' +#define NXPTFA_VP3_SUBVERSION "00" + +struct nxpTfaFWVer { + uint8_t Major; + uint8_t minor; + uint8_t minor_update:6; + uint8_t Update:2; +}; + +struct nxpTfaCmdID { + int a; + //uint16_t a:8; + //uint16_t b:8; + //uint16_t c:8; +}; + +struct nxpTfaFWMsg { + struct nxpTfaFWVer fwVersion; + struct nxpTfaMsg payload; +}; + +typedef struct nxpTfaLiveData { + uint8_t liveData_size; + char name[25]; + char addrs[25]; + int tracker; + int scalefactor[MEMTRACK_MAX_WORDS]; +} nxpTfaLiveData_t; + +#define NXPTFA_SP3_VERSION '3' +#define NXPTFA_SP3_SUBVERSION "00" +struct nxpTfaSpeakerFileMax2 { + nxpTfaHeader_t hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint8_t ohm_primary; + uint8_t ohm_secondary; + struct nxpTfaFWMsg FWmsg; //payload including FW ver and Cmd ID +}; +#endif + +/* + * parameter container file + */ +/* + * descriptors + */ +typedef enum nxpTfaDescriptorType { + dscDevice, // device list + dscProfile, // profile list + dscRegister, // register patch + dscString, // ascii, zero terminated string + dscFile, // filename + file contents + dscPatch, // patch file + dscMarker, // marker to indicate end of a list + dscMode, +#if 1 // TODO: remove me? + dscSetInputSelect, + dscSetOutputSelect, + dscSetProgramConfig, + dscSetLagW, + dscSetGains, + dscSetvBatFactors, + dscSetSensesCal, + dscSetSensesDelay, +#endif + dscBitfield, + dscDefault, // used to reset bitfields to there default values + dscLiveData, + dscLiveDataString, + dscGroup, + dscCmd, +#if 1 // TODO: remove me? + dscSetMBDrc, +#endif + dscFilter, + dscNoInit, + dscFeatures, + dscCfMem, // coolflux memory x,y,io + dsc_last // trailer +} nxpTfaDescriptorType_t; + +#define TFA_BITFIELDDSCMSK 0x7fffffff + +typedef struct nxpTfaDescPtr { + uint32_t offset:24; + uint32_t type:8; // (== enum nxpTfaDescriptorType, assure 8bits length) +}nxpTfaDescPtr_t; + +/* + * generic file descriptor + */ +typedef struct nxpTfaFileDsc { + nxpTfaDescPtr_t name; + uint32_t size; // file data length in bytes + uint8_t data[]; //payload +} nxpTfaFileDsc_t; + + +/* + * device descriptor list + */ +typedef struct nxpTfaDeviceList { + uint8_t length; // nr of items in the list + uint8_t bus; // bus + uint8_t dev; // device + uint8_t func; // subfunction or subdevice + uint32_t devid; // device hw fw id + nxpTfaDescPtr_t name; // device name + nxpTfaDescPtr_t list[]; // items list +} nxpTfaDeviceList_t; + +/* + * profile descriptor list + */ +typedef struct nxpTfaProfileList { + uint32_t length:8; // nr of items in the list + name + uint32_t group:8; // profile group number + uint32_t ID:16; // profile ID + nxpTfaDescPtr_t name; // profile name + nxpTfaDescPtr_t list[]; // items list (lenght-1 items) +} nxpTfaProfileList_t; +#define TFA_PROFID 0x1234 + +/* + * livedata descriptor list + */ +typedef struct nxpTfaLiveDataList { + uint32_t length:8; // nr of items in the list + uint32_t ID:24; // profile ID + nxpTfaDescPtr_t name; // livedata name + nxpTfaDescPtr_t list[]; // items list +} nxpTfaLiveDataList_t; +#define TFA_LIVEDATAID 0x5678 + +/* + * Bitfield descriptor + */ +typedef struct nxpTfaBitfield { + uint16_t value; + uint16_t field; // ==datasheet defined, 16 bits +} nxpTfaBitfield_t; + +/* + * Bitfield enumuration bits descriptor + */ +typedef struct nxpTfaBfEnum { + unsigned int len:4; // this is the actual length-1 + unsigned int pos:4; + unsigned int address:8; +} nxpTfaBfEnum_t; + +/* + * Register patch descriptor + */ +typedef struct nxpTfaRegpatch { + uint8_t address; // register address + uint16_t value; // value to write + uint16_t mask; // mask of bits to write +} nxpTfaRegpatch_t; + +/* + * Mode descriptor + */ +typedef struct nxpTfaUseCase { + int value; // mode value, maps to enum Tfa98xx_Mode +} nxpTfaMode_t; + +/* + * NoInit descriptor + */ +typedef struct nxpTfaNoInit { + uint8_t value; // noInit value +} nxpTfaNoInit_t; + +/* + * Features descriptor + */ +typedef struct nxpTfaFeatures { + uint16_t value[3]; // features value +} nxpTfaFeatures_t; + +typedef struct nxpTfaCmd { + uint16_t id; + unsigned char value[3]; +} nxpTfaCmd_t; + +/* + * the container file + * - the size field is 32bits long (generic=16) + * - all char types are in ASCII + */ +#define NXPTFA_PM_VERSION '1' +#define NXPTFA_PM3_VERSION '3' +#define NXPTFA_PM_SUBVERSION '1' +typedef struct nxpTfaContainer { + char id[2]; // "XX" : XX=type + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint32_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data + uint16_t rev; // "extra chars for rev nr" + char customer[8]; // “name of customer” + char application[8]; // “application name” + char type[8]; // “application type name” + uint16_t ndev; // "nr of device lists" + uint16_t nprof; // "nr of profile lists" + uint16_t nliveData; // "nr of livedata lists" + nxpTfaDescPtr_t index[]; // start of item index table +} nxpTfaContainer_t; + + +#pragma pack (pop) + +/* + * bitfield enums (generated from tfa9890) + */ + + +#endif /* TFA98XXPARAMETERS_H_ */ diff --git a/sound/soc/codecs/tfa9891/tfa98xx_temp.c b/sound/soc/codecs/tfa9891/tfa98xx_temp.c new file mode 100755 index 000000000000..8adbfae78b7d --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa98xx_temp.c @@ -0,0 +1,3419 @@ +/* + * tfa98xx.c tfa98xx codec module + * + * Copyright (c) 2015 NXP Semiconductors + * + * Author: Sebastien Jan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) "%s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#define I2C_RETRIES 50 +#define I2C_RETRY_DELAY 5 /* ms */ +/* TODO: remove genregs usage? */ +#ifdef N1A +#include "tfa98xx_genregs_N1A12.h" +#else +#include "tfa98xx_genregs_N1C.h" +#endif +#include "tfa9891_genregs.h" + +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" +#include "tfa.h" +#include "tfa_service.h" +#include "tfa_container.h" +#include "tfa98xx_parameters.h" + +#define TFA98XX_VERSION "2.10.2" + +/* Change volume selection behavior: + * Uncomment following line to generate a profile change when updating + * a volume control (also changes to the profile of the modified volume + * control) + */ +/*#define TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL 1 +*/ + +/* Supported rates and data formats */ +#define TFA98XX_RATES SNDRV_PCM_RATE_8000_48000 + +//#define TFA98XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +#define TFA98XX_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +#define TF98XX_MAX_DSP_START_TRY_COUNT 10 + +#define XMEM_TAP_ACK 0x0122 +#define XMEM_TAP_READ 0x010f + +static LIST_HEAD(profile_list); /* list of user selectable profiles */ + +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif + +static int tfa98xx_kmsg_regs = 0; +static int tfa98xx_ftrace_regs = 0; + +static struct tfa98xx *tfa98xx_devices[4] = {NULL, NULL, NULL, NULL}; +static int tfa98xx_registered_handles = 0; +static int tfa98xx_vsteps[4]={0,0,0,0}; +static int tfa98xx_profile = 0; /* store profile */ +static int tfa98xx_prof_vsteps[10] = {0}; /* store vstep per profile (single device) */ +static int tfa98xx_mixer_profiles = 0; /* number of user selectable profiles */ +static int tfa98xx_mixer_profile = 0; /* current mixer profile */ + +static char *dflt_prof_name = ""; +module_param(dflt_prof_name, charp, S_IRUGO); + +static int no_start = 0; +module_param(no_start, int, S_IRUGO); +MODULE_PARM_DESC(no_start, "do not start the work queue; for debugging via user\n"); + +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx); +static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx); +static int tfa98xx_get_fssel(unsigned int rate); + +static int get_profile_from_list(char *buf, int id); +static int get_profile_id_for_sr(int id, unsigned int rate); + +struct tfa98xx_rate { + unsigned int rate; + unsigned int fssel; +}; + +static struct tfa98xx_rate rate_to_fssel[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, +}; + +/* Wrapper for tfa start */ +static enum tfa_error tfa98xx_tfa_start(struct tfa98xx *tfa98xx, int next_profile, int *vstep) +{ + enum tfa_error err; + + err = tfa_start(next_profile, vstep); + + /* Check and update tap-detection state (in case of profile change) */ + tfa98xx_tapdet_check_update(tfa98xx); + + /* A cold start erases the configuration, including interrupts setting. + * Restore it if required + */ + tfa98xx_interrupt_restore(tfa98xx); + + return err; +} + +static int tfa98xx_input_open(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + dev_dbg(tfa98xx->codec->dev, "opening device file\n"); + + /* note: open function is called only once by the framework. + * No need to count number of open file instances. + */ + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + dev_dbg(&tfa98xx->i2c->dev, + "DSP not loaded, cannot start tap-detection\n"); + return -EIO; + } + + /* enable tap-detection service */ + tfa98xx->tapdet_open = true; + tfa98xx_tapdet_check_update(tfa98xx); + + return 0; +} + +static void tfa98xx_input_close(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + + dev_dbg(tfa98xx->codec->dev, "closing device file\n"); + + /* Note: close function is called if the device is unregistered */ + + /* disable tap-detection service */ + tfa98xx->tapdet_open = false; + tfa98xx_tapdet_check_update(tfa98xx); +} + +static int tfa98xx_register_inputdev(struct tfa98xx *tfa98xx) +{ + int err; + struct input_dev *input; + input = input_allocate_device(); + + if (!input) { + dev_err(tfa98xx->codec->dev, "Unable to allocate input device\n"); + return -ENOMEM; + } + + input->evbit[0] = BIT_MASK(EV_KEY); + input->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + input->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + input->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + input->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + input->keybit[BIT_WORD(BTN_4)] |= BIT_MASK(BTN_4); + input->keybit[BIT_WORD(BTN_5)] |= BIT_MASK(BTN_5); + input->keybit[BIT_WORD(BTN_6)] |= BIT_MASK(BTN_6); + input->keybit[BIT_WORD(BTN_7)] |= BIT_MASK(BTN_7); + input->keybit[BIT_WORD(BTN_8)] |= BIT_MASK(BTN_8); + input->keybit[BIT_WORD(BTN_9)] |= BIT_MASK(BTN_9); + + input->open = tfa98xx_input_open; + input->close = tfa98xx_input_close; + + input->name = "tfa98xx-tapdetect"; + + input->id.bustype = BUS_I2C; + input_set_drvdata(input, tfa98xx); + + err = input_register_device(input); + if (err) { + dev_err(tfa98xx->codec->dev, "Unable to register input device\n"); + goto err_free_dev; + } + + dev_dbg(tfa98xx->codec->dev, "Input device for tap-detection registered: %s\n", + input->name); + tfa98xx->input = input; + return 0; + +err_free_dev: + input_free_device(input); + return err; +} + +/* + * Check if an input device for tap-detection can and shall be registered. + * Register it if appropriate. + * If already registered, check if still relevant and remove it if necessary. + * unregister: true to request inputdev unregistration. + */ +static void __tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx, bool unregister) +{ + bool tap_profile = false; + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strstr(tfaContProfileName(tfa98xx->handle, i), ".tap")) { + tap_profile = true; + tfa98xx->tapdet_profiles |= 1 << i; + dev_info(tfa98xx->codec->dev, + "found a tap-detection profile (%d - %s)\n", + i, tfaContProfileName(tfa98xx->handle, i)); + } + } + + /* Check for device support: + * - at device level + * - at container (profile) level + */ + if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) || + !tap_profile || + unregister) { + /* No input device supported or required */ + if (tfa98xx->input) { + input_unregister_device(tfa98xx->input); + tfa98xx->input = NULL; + } + return; + } + + /* input device required */ + if (tfa98xx->input) + dev_info(tfa98xx->codec->dev, "Input device already registered, skipping\n"); + else + tfa98xx_register_inputdev(tfa98xx); +} + +static void tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, false); +} + +static void tfa98xx_inputdev_unregister(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, true); +} + +#ifdef CONFIG_DEBUG_FS +/* OTC reporting + * Returns the MTP0 OTC bit value + */ +static int tfa98xx_dbgfs_otc_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc); + enum Tfa98xx_Error err, status; + unsigned short value; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + + err = tfa98xx_get_mtp(tfa98xx->handle, &value); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (otc->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + if (otc->rd_valid) { + /* read cached value */ + *val = otc->rd_value; + pr_err("Returning cached value of OTC: %llu\n", *val); + } else { + pr_info("OTC value never read!\n"); + return -EIO; + } + return 0; + } + } + + *val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS; + pr_err("OTC : %d\n", value&1); + + if (otc->deferrable) { + otc->rd_value = *val; + otc->rd_valid = true; + } + + return 0; +} + +static int tfa98xx_dbgfs_otc_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *otc = &(handles_local[tfa98xx->handle].dev_ops.controls.otc); + enum Tfa98xx_Error err, status; + + if (val != 0 && val != 1) { + pr_err("Unexpected value %llu\n\n", val); + return -EINVAL; + } + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_set_mtp(tfa98xx->handle, + (val << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS) + & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK, + TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (otc->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + /* defer OTC */ + otc->wr_value = val; + otc->triggered = true; + pr_err("Deferring write to OTC (%d)\n", otc->wr_value); + return 0; + } + } + + /* deferrable: cache the value for subsequent offline read */ + if (otc->deferrable) { + otc->rd_value = val; + otc->rd_valid = true; + } + + pr_err("otc < %llu\n", val); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error err, status; + unsigned short value; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_get_mtp(tfa98xx->handle, &value); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != Tfa98xx_Error_Ok) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } + + *val = (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS; + pr_err("MTPEX : %d\n", value & 2 >> 1); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *mtpex = &(handles_local[tfa98xx->handle].dev_ops.controls.mtpex); + enum Tfa98xx_Error err, status; + + if (val != 0) { + pr_err("Can only clear MTPEX (0 value expected)\n"); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + err = tfa98xx_set_mtp(tfa98xx->handle, 0, + TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if (mtpex->deferrable) { + if (err != Tfa98xx_Error_Ok && err != Tfa98xx_Error_NoClock) { + pr_err("Unable to check DSP access: %d\n", err); + return -EIO; + } else if (err == Tfa98xx_Error_NoClock) { + /* defer OTC */ + mtpex->wr_value = 0; + mtpex->triggered = true; + pr_err("Deferring write to MTPEX (%d)\n", mtpex->wr_value); + return 0; + } + } + + pr_err("mtpex < 0\n"); + + return 0; +} + +static int tfa98xx_dbgfs_temp_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error status; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + *val = tfa98xx_get_exttemp(tfa98xx->handle); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + return 0; +} + +static int tfa98xx_dbgfs_temp_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum Tfa98xx_Error status; + + mutex_lock(&tfa98xx->dsp_lock); + status = tfa98xx_open(tfa98xx->handle); + if (status) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + tfa98xx_set_exttemp(tfa98xx->handle, (short)val); + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + return 0; +} + +/* + * calibration: + * write key phrase to the 'calibration' file to trigger a new calibration + * read the calibration file once to get the calibration result + */ +/* tfa98xx_deferred_calibration_status - called from tfaRunWaitCalibration */ +void tfa98xx_deferred_calibration_status(Tfa98xx_handle_t handle, int calibrateDone) +{ + struct tfa98xx *tfa98xx = tfa98xx_devices[handle]; + struct tfa98xx_control *calib = &(handles_local[handle].dev_ops.controls.calib); + + if (calib->wr_value) { + /* a calibration was programmed from the calibration file + * interface + */ + switch (calibrateDone) { + case 1: + /* calibration complete ! */ + calib->wr_value = false; /* calibration over */ + calib->rd_valid = true; /* result available */ + calib->rd_value = true; /* result valid */ + tfa_dsp_get_calibration_impedance(tfa98xx->handle); + wake_up_interruptible(&tfa98xx->wq); + break; + case 0: + pr_info("Calibration not complete, still waiting...\n"); + break; + case -1: + pr_info("Calibration failed\n"); + calib->wr_value = false; /* calibration over */ + calib->rd_valid = true; /* result available */ + calib->rd_value = false; /* result not valid */ + wake_up_interruptible(&tfa98xx->wq); + break; + default: + pr_info("Unknown calibration status: %d\n", + calibrateDone); + } + } +} + +static ssize_t tfa98xx_dbgfs_start_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib); + char *str; + int ret; + + ret = wait_event_interruptible(tfa98xx->wq, calib->wr_value == false); + + if (ret == -ERESTARTSYS) { + /* interrupted by signal */ + return ret; + } + + if (!calib->rd_valid) + /* no calibration result available - skip */ + return 0; + + if (calib->rd_value) { + /* Calibration already complete, return result */ + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) + return -ENOMEM; + ret = print_calibration(tfa98xx->handle, str, PAGE_SIZE); + if (ret < 0) { + kfree(str); + return ret; + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + + pr_err("%s", str); + kfree(str); + calib->rd_value = false; + } else { + /* Calibration failed, return the error code */ + const char estr[] = "-1\n"; + ret = copy_to_user(user_buf, estr, sizeof(estr)); + if (ret) + return -EFAULT; + ret = sizeof(estr); + } + calib->rd_valid = false; + return ret; +} + +static ssize_t tfa98xx_dbgfs_start_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + struct tfa98xx_control *calib = &(handles_local[tfa98xx->handle].dev_ops.controls.calib); + enum Tfa98xx_Error ret; + char buf[32]; + const char ref[] = "please calibrate now"; + int buf_size; + + /* check string length, and account for eol */ + if (count > sizeof(ref) + 1 || count < (sizeof(ref) - 1)) + return -EINVAL; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare string, excluding the trailing \0 and the potentials eol */ + if (strncmp(buf, ref, sizeof(ref) - 1)) + return -EINVAL; + + /* Do not open/close tfa98xx: not required by tfa_clibrate */ + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_calibrate(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + + if(ret) { + pr_info("Calibration start failed (%d), deferring...\n", ret); + calib->triggered = true; + } else { + pr_info("Calibration started\n"); + } + calib->wr_value = true; /* request was triggered from here */ + calib->rd_valid = false; /* result not available */ + calib->rd_value = false; /* result not valid (dafault) */ + + return count; +} + +static ssize_t tfa98xx_dbgfs_r_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char *str; + uint16_t status; + int ret, calibrate_done; + + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_open(tfa98xx->handle); + if (ret) { + mutex_unlock(&tfa98xx->dsp_lock); + return -EBUSY; + } + + /* Need to ensure DSP is access-able, use mtp read access for this + * purpose + */ + ret = tfa98xx_get_mtp(tfa98xx->handle, &status); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + ret = tfaRunWaitCalibration(tfa98xx->handle, &calibrate_done); + if (ret) { + ret = -EIO; + goto r_c_err; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto r_c_err; + } + + switch (calibrate_done) { + case 1: + /* calibration complete ! */ + tfa_dsp_get_calibration_impedance(tfa98xx->handle); + ret = print_calibration(tfa98xx->handle, str, PAGE_SIZE); + break; + case 0: + case -1: + ret = scnprintf(str, PAGE_SIZE, "%d\n", calibrate_done); + break; + default: + pr_err("Unknown calibration status: %d\n", calibrate_done); + ret = -EINVAL; + } + pr_err("calib_done: %d - ret = %d - %s", calibrate_done, ret, str); + + if (ret < 0) + goto r_err; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + +r_err: + kfree(str); +r_c_err: + tfa98xx_close(tfa98xx->handle); + mutex_unlock(&tfa98xx->dsp_lock); + return ret; +} + +static ssize_t tfa98xx_dbgfs_version_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + char str[] = TFA98XX_VERSION "\n"; + int ret; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, sizeof(str)); + + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int ret = 0; + char *str; + + switch (tfa98xx->dsp_init) { + case TFA98XX_DSP_INIT_STOPPED: + str = "Stopped\n"; + break; + case TFA98XX_DSP_INIT_RECOVER: + str = "Recover requested\n"; + break; + case TFA98XX_DSP_INIT_FAIL: + str = "Failed init\n"; + break; + case TFA98XX_DSP_INIT_PENDING: + str = "Pending init\n"; + break; + case TFA98XX_DSP_INIT_DONE: + str = "Init complete\n"; + break; + default: + str = "Invalid\n"; + } + ret = simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error ret; + char buf[32]; + const char start_cmd[] = "start"; + const char stop_cmd[] = "stop"; + const char mon_start_cmd[] = "monitor start"; + const char mon_stop_cmd[] = "monitor stop"; + int buf_size; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare strings, excluding the trailing \0 */ + if (!strncmp(buf, start_cmd, sizeof(start_cmd) - 1)) { + pr_info("Manual triggering of dsp start...\n"); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + mutex_unlock(&tfa98xx->dsp_lock); + pr_err("tfa_start complete: %d\n", ret); + } else if (!strncmp(buf, stop_cmd, sizeof(stop_cmd) - 1)) { + pr_info("Manual triggering of dsp stop...\n"); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_stop(); + mutex_unlock(&tfa98xx->dsp_lock); + pr_err("tfa_stop complete: %d\n", ret); + } else if (!strncmp(buf, mon_start_cmd, sizeof(mon_start_cmd) - 1)) { + pr_info("Manual start of monitor thread...\n"); + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, HZ); + } else if (!strncmp(buf, mon_stop_cmd, sizeof(mon_stop_cmd) - 1)) { + pr_info("Manual stop of monitor thread...\n"); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t tfa98xx_dbgfs_accounting_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char str[255]; + int ret; + int n = 0; + + n += snprintf(&str[n], sizeof(str)-1-n, "Wait4Src\t= %d\n", tfa98xx->count_wait_for_source_state); + n += snprintf(&str[n], sizeof(str)-1-n, "NOCLK\t\t= %d\n", tfa98xx->count_noclk); + + str[n+1] = '\0'; /* in case str is not large enough */ + + ret = simple_read_from_buffer(user_buf, count, ppos, str, n+1); + + return ret; +} + +static int tfa98xx_dbgfs_pga_gain_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int err; + unsigned int value; + +/* *val = TFA_GET_BF(tfa98xx->handle, SAAMGAIN);*/ + err = regmap_read(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA, &value); + *val = (value & TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK) >> + TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS; + return 0; +} + +static int tfa98xx_dbgfs_pga_gain_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int err; + unsigned int value; + + value = val & 0xffff; + if (value > 7) + return -EINVAL; +/* TFA_SET_BF(tfa98xx->handle, SAAMGAIN, value);*/ + err = regmap_update_bits(tfa98xx->regmap, TFA98XX_CTRL_SAAM_PGA, + TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK, + value << TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS); + return err; +} + +/* Direct registers access - provide register address in hex */ +#define TFA98XX_DEBUGFS_REG_SET(__reg) \ +static int tfa98xx_dbgfs_reg_##__reg##_set(void *data, u64 val) \ +{ \ + struct i2c_client *i2c = (struct i2c_client *)data; \ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \ + unsigned int ret, value; \ + \ + ret = regmap_write(tfa98xx->regmap, 0x##__reg, (val & 0xffff)); \ + value = val & 0xffff; \ + return 0; \ +} \ +static int tfa98xx_dbgfs_reg_##__reg##_get(void *data, u64 *val) \ +{ \ + struct i2c_client *i2c = (struct i2c_client *)data; \ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); \ + unsigned int value; \ + int ret; \ + ret = regmap_read(tfa98xx->regmap, 0x##__reg, &value); \ + *val = value; \ + return 0; \ +} \ +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_reg_##__reg##_fops, tfa98xx_dbgfs_reg_##__reg##_get, \ + tfa98xx_dbgfs_reg_##__reg##_set, "0x%llx\n"); + +#define VAL(str) #str +#define TOSTRING(str) VAL(str) +#define TFA98XX_DEBUGFS_REG_CREATE_FILE(__reg, __name) \ + debugfs_create_file(TOSTRING(__reg) "-" TOSTRING(__name), S_IRUGO|S_IWUGO, dbg_reg_dir,\ + i2c, &tfa98xx_dbgfs_reg_##__reg##_fops); + + +TFA98XX_DEBUGFS_REG_SET(00); +TFA98XX_DEBUGFS_REG_SET(01); +TFA98XX_DEBUGFS_REG_SET(02); +TFA98XX_DEBUGFS_REG_SET(03); +TFA98XX_DEBUGFS_REG_SET(04); +TFA98XX_DEBUGFS_REG_SET(05); +TFA98XX_DEBUGFS_REG_SET(06); +TFA98XX_DEBUGFS_REG_SET(07); +TFA98XX_DEBUGFS_REG_SET(08); +TFA98XX_DEBUGFS_REG_SET(09); +TFA98XX_DEBUGFS_REG_SET(0A); +TFA98XX_DEBUGFS_REG_SET(0B); +TFA98XX_DEBUGFS_REG_SET(0F); +TFA98XX_DEBUGFS_REG_SET(10); +TFA98XX_DEBUGFS_REG_SET(11); +TFA98XX_DEBUGFS_REG_SET(12); +TFA98XX_DEBUGFS_REG_SET(13); +TFA98XX_DEBUGFS_REG_SET(22); +TFA98XX_DEBUGFS_REG_SET(25); + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_otc_fops, tfa98xx_dbgfs_otc_get, + tfa98xx_dbgfs_otc_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_mtpex_fops, tfa98xx_dbgfs_mtpex_get, + tfa98xx_dbgfs_mtpex_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_temp_fops, tfa98xx_dbgfs_temp_get, + tfa98xx_dbgfs_temp_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_pga_gain_fops, tfa98xx_dbgfs_pga_gain_get, + tfa98xx_dbgfs_pga_gain_set, "%llu\n"); + +static const struct file_operations tfa98xx_dbgfs_calib_start_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_start_get, + .write = tfa98xx_dbgfs_start_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_r_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_r_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_version_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_version_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_dsp_state_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_dsp_state_get, + .write = tfa98xx_dbgfs_dsp_state_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_accounting_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_accounting_get, + .llseek = default_llseek, +}; + + +static void tfa98xx_debug_init(struct tfa98xx *tfa98xx, struct i2c_client *i2c) +{ + char name[50]; + struct dentry *dbg_reg_dir; + + scnprintf(name, MAX_CONTROL_NAME, "%s-%x", i2c->name, i2c->addr); + tfa98xx->dbg_dir = debugfs_create_dir(name, NULL); + debugfs_create_file("OTC", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_otc_fops); + debugfs_create_file("MTPEX", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_mtpex_fops); + debugfs_create_file("TEMP", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_temp_fops); + debugfs_create_file("calibrate", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_start_fops); + debugfs_create_file("R", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_r_fops); + debugfs_create_file("version", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_version_fops); + debugfs_create_file("dsp-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_dsp_state_fops); + debugfs_create_file("accounting", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_accounting_fops); + + /* Direct registers access */ + if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV) { + dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir); + TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS); + TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER); + TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT); + TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR); + TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST); + TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION); + TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0A, I2S_SEL_REG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0B, HIDDEN_MTP_KEY2); + TFA98XX_DEBUGFS_REG_CREATE_FILE(0F, INTERRUPT_REG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(10, PDM_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(11, PDM_OUT_CTRL); + TFA98XX_DEBUGFS_REG_CREATE_FILE(12, PDM_DS4_R); + TFA98XX_DEBUGFS_REG_CREATE_FILE(13, PDM_DS4_L); + TFA98XX_DEBUGFS_REG_CREATE_FILE(22, CTRL_SAAM_PGA); + TFA98XX_DEBUGFS_REG_CREATE_FILE(25, MISC_CTRL); + } + + if (tfa98xx->flags & TFA98XX_FLAG_TFA9897_FAM_DEV) { + dbg_reg_dir = debugfs_create_dir("regs", tfa98xx->dbg_dir); + TFA98XX_DEBUGFS_REG_CREATE_FILE(00, STATUS); + TFA98XX_DEBUGFS_REG_CREATE_FILE(01, BATTERYVOLTAGE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(02, TEMPERATURE); + TFA98XX_DEBUGFS_REG_CREATE_FILE(03, REVISIONNUMBER); + TFA98XX_DEBUGFS_REG_CREATE_FILE(04, I2SREG); + TFA98XX_DEBUGFS_REG_CREATE_FILE(05, BAT_PROT); + TFA98XX_DEBUGFS_REG_CREATE_FILE(06, AUDIO_CTR); + TFA98XX_DEBUGFS_REG_CREATE_FILE(07, DCDCBOOST); + TFA98XX_DEBUGFS_REG_CREATE_FILE(08, SPKR_CALIBRATION); + TFA98XX_DEBUGFS_REG_CREATE_FILE(09, SYS_CTRL); + } + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + dev_dbg(tfa98xx->dev, "Adding pga_gain debug interface\n"); + debugfs_create_file("pga_gain", S_IRUGO, tfa98xx->dbg_dir, + tfa98xx->i2c, + &tfa98xx_dbgfs_pga_gain_fops); + } +} + +static void tfa98xx_debug_remove(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->dbg_dir) + debugfs_remove_recursive(tfa98xx->dbg_dir); +} +#endif + +static int tfa98xx_get_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + int vstep = tfa98xx_prof_vsteps[profile]; + ucontrol->value.integer.value[0] = + tfacont_get_max_vstep(tfa98xx->handle, profile) + - vstep - 1; + return 0; +} + +static int tfa98xx_set_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + int value = ucontrol->value.integer.value[0]; + int vstep = tfa98xx_prof_vsteps[profile]; + int vsteps = tfacont_get_max_vstep(tfa98xx->handle, profile); + int new_vstep, err = 0; + int ready = 0; + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + if (no_start != 0) + return 0; + + if (vstep == vsteps - value - 1) + return 0; + + new_vstep = vsteps - value - 1; + + if (new_vstep < 0) + new_vstep = 0; + + tfa98xx_prof_vsteps[profile] = new_vstep; + +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + if (profile == tfa98xx_profile) { +#endif + /* this is the active profile, program the new vstep */ + tfa98xx_vsteps[0] = new_vstep; + tfa98xx_vsteps[1] = new_vstep; + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + /* Enable internal clk (osc1m) to switch profile */ + if ((tfa98xx_dev_family(tfa98xx->handle) == 2) && (ready == 0)) { + /* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */ + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + /* Set polarity to high */ + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + + TFA_SET_BF(tfa98xx->handle, RST, 1); + TFA_SET_BF(tfa98xx->handle, SBSL, 0); + TFA_SET_BF(tfa98xx->handle, AMPC, 0); + TFA_SET_BF(tfa98xx->handle, AMPE, 0); + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1); + ready = 1; + } + + if (ready) { + err = tfa98xx_tfa_start(tfa98xx, profile, tfa98xx_vsteps); + if (err) { + pr_err("Write vstep error: %d\n", err); + } else { + pr_err("Succesfully changed vstep index!\n"); + } + } + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) { + /* Set back to external clock */ + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0); + TFA_SET_BF(tfa98xx->handle, SBSL, 1); + } + + mutex_unlock(&tfa98xx->dsp_lock); +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + } +#endif + + pr_err("vstep:%d, (control value: %d) - profile %d\n", new_vstep, + value, profile); + return (err == 0); +} + +static int tfa98xx_info_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1;// TODO handles_local[dev_idx].spkr_count + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tfacont_get_max_vstep(tfa98xx->handle, profile) - 1; + pr_err("vsteps count: %d [prof=%d]\n", tfacont_get_max_vstep(tfa98xx->handle, profile), + profile); + return 0; +} + +static int tfa98xx_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = tfa98xx_mixer_profile; + return 0; +} + +static int tfa98xx_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + int profile_count = tfa98xx_mixer_profiles; + int profile = tfa98xx_mixer_profile; + int new_profile = ucontrol->value.integer.value[0]; + int err; + int ready = 0; + int prof_idx; + + + if (no_start != 0) + return 0; + + if (new_profile == profile) + return 0; + + if (new_profile >= profile_count) + return 0; + + /* get the container profile for the requested sample rate */ + prof_idx = get_profile_id_for_sr(new_profile, tfa98xx->rate); + if (prof_idx < 0) { + pr_err("tfa98xx: sample rate [%d] not supported for this mixer profile [%d].\n", tfa98xx->rate, new_profile); + return 0; + } + pr_err("selected container profile [%d]\n", prof_idx); + + /* update mixer profile */ + tfa98xx_mixer_profile = new_profile; + + /* update 'real' profile (container profile) */ + tfa98xx_profile = prof_idx; + tfa98xx_vsteps[0] = tfa98xx_prof_vsteps[prof_idx]; + tfa98xx_vsteps[1] = tfa98xx_prof_vsteps[prof_idx]; + + /* + * Don't call tfa_start() on TFA1 if there is no clock. + * For TFA2 is able to load the profile without clock. + */ + + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + /* Enable internal clk (osc1m) to switch profile */ + if (tfa98xx_dev_family(tfa98xx->handle) == 2 && ready == 0) { + /* Disable interrupts (Enabled again in the wrapper function: tfa98xx_tfa_start) */ + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + /* Set polarity to high */ + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + + TFA_SET_BF(tfa98xx->handle, RST, 1); + TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 0); + TFA_SET_BF(tfa98xx->handle, AMPC, 0); + TFA_SET_BF(tfa98xx->handle, AMPE, 0); + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 1); + ready = 1; + } + + if (ready) { + /* Also re-enables the interrupts */ + err = tfa98xx_tfa_start(tfa98xx, prof_idx, tfa98xx_vsteps); + if (err) { + pr_info("Write profile error: %d\n", err); + } else { + pr_err("Changed to profile %d (vstep = %d)\n", prof_idx, + tfa98xx_vsteps[0]); + } + } + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) { + /* Set back to external clock */ + TFA_SET_BF(tfa98xx->handle, REFCKSEL, 0); + TFA_SET_BF_VOLATILE(tfa98xx->handle, SBSL, 1); + } + + mutex_unlock(&tfa98xx->dsp_lock); + + /* Flag DSP as invalidated as the profile change may invalidate the + * current DSP configuration. That way, further stream start can + * trigger a tfa_start. + */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_INVALIDATED; + + return 1; +} + +static struct snd_kcontrol_new *tfa98xx_controls; + +/* copies the profile basename (i.e. part until .) into buf */ +static void get_profile_basename(char* buf, char* profile) +{ + int cp_len = 0, idx = 0; + char *pch; + + pch = strchr(profile, '.'); + idx = pch - profile; + cp_len = (pch != NULL) ? idx : (int) strlen(profile); + memcpy(buf, profile, cp_len); + buf[cp_len] = 0; +} + +/* return the profile name accociated with id from the profile list */ +static int get_profile_from_list(char *buf, int id) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (bprof->item_id == id) { + strcpy(buf, bprof->basename); + return 0; + } + } + + return -1; +} + +/* search for the profile in the profile list */ +static int is_profile_in_list(char *profile, int len) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (0 == strncmp(bprof->basename, profile, len)) + return 1; + } + + return 0; +} + +/* + * for the profile with id, look if the requested samplerate is + * supported, if found return the (container)profile for this + * samplerate, on error or if not found return -1 + */ +static int get_profile_id_for_sr(int id, unsigned int rate) +{ + int idx = 0; + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (id == bprof->item_id) { + idx = tfa98xx_get_fssel(rate); + if (idx < 0) { + /* samplerate not supported */ + return -1; + } + + return bprof->sr_rate_sup[idx]; + } + } + + /* profile not found */ + return -1; +} + +/* check if this profile is a calibration profile */ +static int is_calibration_profile(char *profile) +{ + if (strstr(profile, ".cal") != NULL) + return 1; + return 0; +} + +/* + * adds the (container)profile index of the samplerate found in + * the (container)profile to a fixed samplerate table in the (mixer)profile + */ +static int add_sr_to_profile(struct tfa98xx *tfa98xx, char *basename, int len, int profile) +{ + struct tfa98xx_baseprofile *bprof; + int idx = 0; + unsigned int sr = 0; + + list_for_each_entry(bprof, &profile_list, list) { + if (0 == strncmp(bprof->basename, basename, len)) { + /* add supported samplerate for this profile */ + sr = tfa98xx_get_profile_sr(tfa98xx->handle, profile); + if (!sr) { + pr_err("unable to identify supported sample rate for %s\n", bprof->basename); + return -1; + } + + /* get the index for this samplerate */ + idx = tfa98xx_get_fssel(sr); + if (idx < 0 || idx >= TFA98XX_NUM_RATES) { + pr_err("invalid index for samplerate %d\n", idx); + return -1; + } + + /* enter the (container)profile for this samplerate at the corresponding index */ + bprof->sr_rate_sup[idx] = profile; + + pr_err("added profile:samplerate = [%d:%d] for mixer profile: %s\n", profile, sr, bprof->basename); + } + } + + return 0; +} + +static int tfa98xx_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char profile_name[MAX_CONTROL_NAME] = {0}; + int count = tfa98xx_mixer_profiles, err = -1; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + err = get_profile_from_list(profile_name, uinfo->value.enumerated.item); + if (err != 0) + return -EINVAL; + + strcpy(uinfo->value.enumerated.name, profile_name); + + return 0; +} + +static int tfa98xx_get_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int tfa98xx_set_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#endif + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ready = 0; + + pr_err("%ld\n", ucontrol->value.integer.value[0]); + + tfa98xx_open(tfa98xx->handle); + tfa98xx_dsp_system_stable(tfa98xx->handle, &ready); + tfa98xx_close(tfa98xx->handle); + + if ((ucontrol->value.integer.value[0] != 0) && ready) { + cancel_delayed_work_sync(&tfa98xx->monitor_work); + + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + return 0; + mutex_lock(&tfa98xx->dsp_lock); + tfa_stop(); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } + + ucontrol->value.integer.value[0] = 0; + return 1; +} + +static int tfa98xx_create_controls(struct tfa98xx *tfa98xx) +{ + int prof, nprof, mix_index = 0; + int nr_controls = 0, id = 0; + char *name; + struct tfa98xx_baseprofile *bprofile; + + /* Create the following controls: + * - enum control to select the active profile + * - one volume control for each profile hosting a vstep + * - Stop control on TFA1 devices + */ + + nr_controls = 1; /* Profile control */ + if (tfa98xx_dev_family(tfa98xx->handle) == 1) + nr_controls += 1; /* Stop control */ + + /* allocate the tfa98xx_controls base on the nr of profiles */ + nprof = tfaContMaxProfile(tfa98xx->handle); + + for (prof = 0; prof < nprof; prof++) { + if (tfacont_get_max_vstep(tfa98xx->handle, prof)) + nr_controls++; /* Playback Volume control */ + } + + tfa98xx_controls = devm_kzalloc(tfa98xx->codec->dev, + nr_controls * sizeof(tfa98xx_controls[0]), GFP_KERNEL); + if(!tfa98xx_controls) + return -ENOMEM; + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + scnprintf(name, MAX_CONTROL_NAME, "%s Profile", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_profile; + tfa98xx_controls[mix_index].get = tfa98xx_get_profile; + tfa98xx_controls[mix_index].put = tfa98xx_set_profile; + // tfa98xx_controls[mix_index].private_value = profs; /* save number of profiles */ + mix_index++; + + /* create mixer items for each profile that has volume */ + for (prof = 0; prof < nprof; prof++) { + /* create an new empty profile */ + bprofile = devm_kzalloc(tfa98xx->codec->dev, sizeof(*bprofile), GFP_KERNEL); + if (!bprofile) + return -ENOMEM; + + bprofile->len = 0; + bprofile->item_id = -1; + INIT_LIST_HEAD(&bprofile->list); + + /* copy profile name into basename until the . */ + get_profile_basename(bprofile->basename, tfaContProfileName(tfa98xx->handle, prof)); + bprofile->len = strlen(bprofile->basename); + + /* + * search the profile list for a profile with basename, if it is not found then + * add it to the list and add a new mixer control (if it has vsteps) + * also, if it is a calibration profile, do not add it to the list + */ + if (is_profile_in_list(bprofile->basename, bprofile->len) == 0 && + is_calibration_profile(tfaContProfileName(tfa98xx->handle, prof)) == 0) { + /* the profile is not present, add it to the list */ + list_add(&bprofile->list, &profile_list); + bprofile->item_id = id++; + + pr_err("profile added [%d]: %s\n", bprofile->item_id, bprofile->basename); + + if (tfacont_get_max_vstep(tfa98xx->handle, prof)) { + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s %s Playback Volume", + tfa98xx->fw.name, bprofile->basename); + + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_vstep; + tfa98xx_controls[mix_index].get = tfa98xx_get_vstep; + tfa98xx_controls[mix_index].put = tfa98xx_set_vstep; + tfa98xx_controls[mix_index].private_value = prof; /* save profile index */ + mix_index++; + } + } + + /* look for the basename profile in the list of mixer profiles and add the + container profile index to the supported samplerates of this mixer profile */ + add_sr_to_profile(tfa98xx, bprofile->basename, bprofile->len, prof); + } + + + if (tfa98xx_dev_family(tfa98xx->handle) == 1) { + /* Create a mixer item for stop control on TFA1 */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s Stop", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = snd_soc_info_bool_ext; + tfa98xx_controls[mix_index].get = tfa98xx_get_stop_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_stop_ctl; + mix_index++; + } + + /* set the number of user selectable profiles in the mixer */ + tfa98xx_mixer_profiles = id; + + return snd_soc_add_codec_controls(tfa98xx->codec, + tfa98xx_controls, mix_index); +} + +static void *tfa98xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + memcpy(str, buf, strlen(buf)); + return str; +} + +static int tfa98xx_append_i2c_address(struct device *dev, + struct i2c_client *i2c, + struct snd_soc_dapm_widget *widgets, + int num_widgets, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = i2c->adapter->nr; + int addr = i2c->addr; + if (dai_drv && num_dai > 0) + for(i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x",dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = tfa98xx_devm_kstrdup(dev, buf); + } + + /* the idea behind this is convert: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + * into: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback-2-36", 0, SND_SOC_NOPM, 0, 0), + */ + if (widgets && num_widgets > 0) + for(i = 0; i < num_widgets; i++) { + if(!widgets[i].sname) + continue; + if((widgets[i].id == snd_soc_dapm_aif_in) + || (widgets[i].id == snd_soc_dapm_aif_out)) { + snprintf(buf, 50, "%s-%x-%x", widgets[i].sname, + i2cbus, addr); + widgets[i].sname = tfa98xx_devm_kstrdup(dev, buf); + } + } + + return 0; +} + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_common[] = { + /* Stream widgets */ + SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "AIF Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_INPUT("AEC Loopback"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_stereo[] = { + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_saam[] = { + SND_SOC_DAPM_INPUT("SAAM MIC"), +}; + +static struct snd_soc_dapm_widget tfa9888_dapm_inputs[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_common[] = { + { "OUTL", NULL, "AIF IN" }, + { "AIF OUT", NULL, "AEC Loopback" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_saam[] = { + { "AIF OUT", NULL, "SAAM MIC" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_stereo[] = { + { "OUTR", NULL, "AIF IN" }, +}; + +static const struct snd_soc_dapm_route tfa9888_input_dapm_routes[] = { + { "AIF OUT", NULL, "DMIC1" }, + { "AIF OUT", NULL, "DMIC2" }, + { "AIF OUT", NULL, "DMIC3" }, + { "AIF OUT", NULL, "DMIC4" }, +}; + +static void tfa98xx_add_widgets(struct tfa98xx *tfa98xx) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,18,0) + struct snd_soc_dapm_context *dapm = &tfa98xx->codec->dapm; +#else + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(tfa98xx->codec); +#endif + struct snd_soc_dapm_widget *widgets; + unsigned int num_dapm_widgets = ARRAY_SIZE(tfa98xx_dapm_widgets_common); + + widgets = devm_kzalloc(&tfa98xx->i2c->dev, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common), + GFP_KERNEL); + if (!widgets) + return; + memcpy(widgets, tfa98xx_dapm_widgets_common, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + + tfa98xx_append_i2c_address(&tfa98xx->i2c->dev, + tfa98xx->i2c, + widgets, + num_dapm_widgets, + NULL, + 0); + + snd_soc_dapm_new_controls(dapm, widgets, + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_common, + ARRAY_SIZE(tfa98xx_dapm_routes_common)); + + if (tfa98xx->flags & TFA98XX_FLAG_STEREO_DEVICE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_stereo, + ARRAY_SIZE(tfa98xx_dapm_widgets_stereo)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_stereo, + ARRAY_SIZE(tfa98xx_dapm_routes_stereo)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_MULTI_MIC_INPUTS) { + snd_soc_dapm_new_controls(dapm, tfa9888_dapm_inputs, + ARRAY_SIZE(tfa9888_dapm_inputs)); + snd_soc_dapm_add_routes(dapm, tfa9888_input_dapm_routes, + ARRAY_SIZE(tfa9888_input_dapm_routes)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_saam, + ARRAY_SIZE(tfa98xx_dapm_widgets_saam)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_saam, + ARRAY_SIZE(tfa98xx_dapm_routes_saam)); + } +} + + +/* Match tfa98xx device structure with a valid DSP handle */ +/* TODO can be removed once we pass the device struct in stead of handles + The check in tfa98xx_register_dsp() is implicitly done in tfa_probe() /tfa98xx_cnt_slave2idx(_) +*/ +static int tfa98xx_register_dsp(struct tfa98xx *tfa98xx) +{ + int i, handle = -1; + u8 slave; + + for (i = 0; i < tfa98xx_cnt_max_device(); i++) { + if (tfaContGetSlave(i, &slave) != Tfa98xx_Error_Ok) + goto reg_err; + pr_err("%s: i=%d - dev = 0x%x\n", __func__, i, slave); + if (slave == tfa98xx->i2c->addr) { + handle = i; + break; + } + } + if (handle != -1) { + tfa98xx_devices[handle] = tfa98xx; + dev_info(&tfa98xx->i2c->dev, + "Registered DSP instance with handle %d\n", + handle); + tfa98xx_registered_handles++; + return handle; + } +reg_err: + dev_err(&tfa98xx->i2c->dev, + "Unable to match I2C address 0x%x with a container device\n", + tfa98xx->i2c->addr); + return -EINVAL; +} + +static void tfa98xx_unregister_dsp(struct tfa98xx *tfa98xx) +{ + tfa98xx_registered_handles--; + + tfa98xx_devices[tfa98xx->handle] = NULL; + dev_info(&tfa98xx->i2c->dev, "Un-registered DSP instance with handle %d\n", + tfa98xx->handle); +} + + +/* I2C wrapper functions */ +enum Tfa98xx_Error tfa98xx_write_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_write(tfa98xx->regmap, subaddress, value); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, " WR reg=0x%02x, val=0x%04x %s\n", + subaddress, value, + ret<0? "Error!!" : ""); + + if(tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\tWR reg=0x%02x, val=0x%04x %s\n", + subaddress, value, + ret<0? "Error!!" : ""); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_read_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short *val) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + unsigned int value; + int retries = I2C_RETRIES; + int ret; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_read(tfa98xx->regmap, subaddress, &value); + if (ret < 0) { + pr_warn("i2c error at subaddress 0x%x, retries left: %d\n", subaddress, retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + *val = value & 0xffff; + + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, "RD reg=0x%02x, val=0x%04x %s\n", + subaddress, *val, + ret<0? "Error!!" : ""); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\tRD reg=0x%02x, val=0x%04x %s\n", + subaddress, *val, + ret<0? "Error!!" : ""); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_read_data(Tfa98xx_handle_t handle, + unsigned char reg, + int len, unsigned char value[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + struct i2c_client *tfa98xx_client; + int err; + int tries = 0; + struct i2c_msg msgs[] = { + { + .flags = 0, + .len = 1, + .buf = ®, + }, { + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + if (tfa98xx_devices[handle] && tfa98xx_devices[handle]->i2c) { + tfa98xx = tfa98xx_devices[handle]; + tfa98xx_client = tfa98xx->i2c; + msgs[0].addr = tfa98xx_client->addr; + msgs[1].addr = tfa98xx_client->addr; + + do { + err = i2c_transfer(tfa98xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&tfa98xx_client->dev, "read transfer error %d\n", + err); + error = Tfa98xx_Error_Fail; + } + + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx_client->dev, "RD-DAT reg=0x%02x, len=%d\n", + reg, len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tRD-DAT reg=0x%02x, len=%d\n", + reg, len); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_write_raw(Tfa98xx_handle_t handle, + int len, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa98xx_devices[handle]) { + tfa98xx = tfa98xx_devices[handle]; +retry: + ret = i2c_master_send(tfa98xx->i2c, data, len); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + if (ret == len) { + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, " WR-RAW len=%d\n", len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tWR-RAW len=%d\n", len); + return Tfa98xx_Error_Ok; + } + pr_err(" WR-RAW (len=%d) Error I2C send size mismatch %d\n", len, ret); + error = Tfa98xx_Error_Fail; + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +/* Read and return status_reg content, and intercept (interrupt related) + * events if any. + * mask can be used to ask to ignore some status bits. + */ +static unsigned int tfa98xx_read_status_reg(struct tfa98xx *tfa98xx, + unsigned int mask) +{ + unsigned int reg; + /* interrupt bits to check */ + unsigned int errs = TFA98XX_STATUSREG_WDS | + TFA98XX_STATUSREG_SPKS; + + regmap_read(tfa98xx->regmap, TFA98XX_STATUSREG, ®); + + if (reg & errs & ~mask) { + /* interesting status bits to handle. Just trace for now. */ + dev_info(tfa98xx->codec->dev, "status_reg events: 0x%x\n", reg); + } + + return reg; +} + +/* Interrupts management */ + +static void tfa98xx_interrupt_restore_tfa2(struct tfa98xx *tfa98xx) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + /* Write interrupt enable registers */ + regmap_write(tfa98xx->regmap, base_addr_inten + 0, + handles_local[tfa98xx->handle].interrupt_enable[0]); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, + handles_local[tfa98xx->handle].interrupt_enable[1]); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, + handles_local[tfa98xx->handle].interrupt_enable[2]); +} + +static void tfa98xx_interrupt_enable_tfa2(struct tfa98xx *tfa98xx, bool enable) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + + if (enable) { + tfa98xx_interrupt_restore_tfa2(tfa98xx); + } else { + regmap_write(tfa98xx->regmap, base_addr_inten + 0, 0); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, 0); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, 0); + } +} + +/* Check if tap-detection can and shall be enabled. + * Configure SPK interrupt accordingly or setup polling mode + * Tap-detection shall be active if: + * - the service is enabled (tapdet_open), AND + * - the current profile is a tap-detection profile + * On TFA1 familiy of devices, activating tap-detection means enabling the SPK + * interrupt if available. + * We also update the tapdet_enabled and tapdet_poll variables. + */ +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx) +{ + unsigned int spkerr, enable = false; + unsigned int err; + int val, count = 0; + + /* Support tap-detection on TFA1 family of devices */ + if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) || + (tfa98xx_dev_family(tfa98xx->handle)) != 1) + return; + + if (tfa98xx->tapdet_open && + (tfa98xx->tapdet_profiles & (1 << tfa98xx_profile))) + enable = true; + + spkerr = enable ? 0 : 1; + + if (!gpio_is_valid(tfa98xx->irq_gpio)) { + /* interrupt not available, setup polling mode */ + tfa98xx->tapdet_poll = true; + if (enable) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->tapdet_work, HZ/10); + else + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + dev_dbg(tfa98xx->codec->dev, + "Polling for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx_profile); + + } else { + dev_dbg(tfa98xx->codec->dev, + "SPK interrupt for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx_profile); + + /* update status_reg mask to match enabled interrupts */ + handles_local[tfa98xx->handle].interrupt_status[0] &= + ~TFA98XX_STATUSREG_SPKS; + handles_local[tfa98xx->handle].interrupt_status[0] |= + enable << TFA98XX_STATUSREG_SPKS_POS; + + /* update interrupt_reg to match enabled interrupts */ + handles_local[tfa98xx->handle].interrupt_enable[0] &= + ~TFA98XX_INTERRUPT_REG_SPKD; + handles_local[tfa98xx->handle].interrupt_enable[0] |= + spkerr << TFA98XX_INTERRUPT_REG_SPKD_POS; + } + + /* check disabled => enabled transition to clear pending events */ + if (!tfa98xx->tapdet_enabled && enable) { + /* clear pending event if any */ + err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0, + Tfa98xx_DMEM_XMEM); + if (err) + pr_info("Unable to write to XMEM\n"); + + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) { + val = tfa98xx_read_status_reg(tfa98xx, + TFA98XX_STATUSREG_SPKS); + count++; + } + if (count > 1) + pr_info("Had to run %d times to ack SPKS at init\n", count); + } + + tfa98xx->tapdet_enabled = enable; + + if (!tfa98xx->tapdet_poll) + tfa98xx_interrupt_restore(tfa98xx); +} + +/* Initial configuration of interrupt masks of devices for TFA1 family + * Disable all interrupts by default. + */ +static void tfa98xx_interrupt_setup_tfa1(struct tfa98xx *tfa98xx) +{ + uint16_t ie_reg = 0; + + /* disable all interrupt sources */ + ie_reg = TFA98XX_INTERRUPT_REG_VDDD | + TFA98XX_INTERRUPT_REG_OTDD | + TFA98XX_INTERRUPT_REG_OVDD | + TFA98XX_INTERRUPT_REG_UVDD | + TFA98XX_INTERRUPT_REG_OCDD | + TFA98XX_INTERRUPT_REG_CLKD | + TFA98XX_INTERRUPT_REG_DCCD | + TFA98XX_INTERRUPT_REG_SPKD | + TFA98XX_INTERRUPT_REG_WDD; + /* preserve reserved value */ + ie_reg |= 1 << 9; + + /* Store requested setup */ + handles_local[tfa98xx->handle].interrupt_enable[0] = ie_reg; + handles_local[tfa98xx->handle].interrupt_status[0] = 0; + + dev_dbg(&tfa98xx->i2c->dev, "Initial interrupts setup: ICR = 0x%04x\n", ie_reg); +} + +/* Restore for 1st generation of devices */ +static void tfa98xx_interrupt_restore_tfa1(struct tfa98xx *tfa98xx) +{ + unsigned int ie_reg = 0; + + regmap_read(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, &ie_reg); + + if (ie_reg != handles_local[tfa98xx->handle].interrupt_enable[0]) { + ie_reg = handles_local[tfa98xx->handle].interrupt_enable[0]; + + /* Write interrupt enable registers */ + regmap_write(tfa98xx->regmap, TFA98XX_INTERRUPT_REG, ie_reg); + + dev_dbg(&tfa98xx->i2c->dev, "Restored interrupts: ICR = 0x%04x\n", + ie_reg); + } else { + dev_dbg(&tfa98xx->i2c->dev, "No interrupt restore needed\n"); + } + +} + +/* Enable for 1st generation of devices */ +static void tfa98xx_interrupt_enable_tfa1(struct tfa98xx *tfa98xx, bool enable) +{ + handles_local[tfa98xx->handle].interrupt_enable[0] &= ~TFA98XX_INTERRUPT_REG_INT; + handles_local[tfa98xx->handle].interrupt_enable[0] |= enable << TFA98XX_INTERRUPT_REG_INT_POS; + + tfa98xx_interrupt_restore_tfa1(tfa98xx); +} + +static void tfa98xx_interrupt_setup_tfa2(struct tfa98xx *tfa98xx) +{ + uint16_t ie_reg; + + handles_local[tfa98xx->handle].interrupt_enable[0] = 0; + ie_reg = 0; + TFA_SET_BF_VALUE(tfa98xx->handle, IEMWSRC, 1, &ie_reg); + handles_local[tfa98xx->handle].interrupt_enable[1] = ie_reg; + handles_local[tfa98xx->handle].interrupt_enable[2] = 0; +} + +/* Initial SW configuration for interrupts. Does not enable HW interrupts. */ +static void tfa98xx_interrupt_setup(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx->flags & TFA98XX_FLAG_TFA9890_FAM_DEV) + tfa98xx_interrupt_setup_tfa1(tfa98xx); + else + tfa98xx_interrupt_setup_tfa2(tfa98xx); +} + +/* Restore interrupt setup in case it would be lost (at device cold-start) */ +static void tfa98xx_interrupt_restore(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_interrupt_restore_tfa2(tfa98xx); + else + tfa98xx_interrupt_restore_tfa1(tfa98xx); +} + +/* global enable / disable interrupts */ +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_interrupt_enable_tfa2(tfa98xx, enable); + else + tfa98xx_interrupt_enable_tfa1(tfa98xx, enable); +} + +/* Firmware management + * Downloaded once only at module init + * FIXME: may need to review that (one per instance of codec device?) + */ +static char *fw_name = "tfa98xx.cnt"; +module_param(fw_name, charp, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(fw_name, "TFA98xx DSP firmware (container file) name."); + +static nxpTfaContainer_t *container; +#if 0 +static void tfa98xx_container_loaded(const struct firmware *cont, void *context) +{ + struct tfa98xx *tfa98xx = context; + enum tfa_error tfa_err; + int container_size; + int handle; + int ret; + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + if (!cont) { + pr_err("Failed to read %s\n", fw_name); + return; + } + + pr_err("loaded %s - size: %zu\n", fw_name, + cont ? cont->size : 0); + + container = kzalloc(cont->size, GFP_KERNEL); + if (!container) { + release_firmware(cont); + pr_err("Error allocating memory\n"); + return; + } + + container_size = cont->size; + memcpy(container, cont->data, container_size); + release_firmware(cont); + + pr_err("%.2s%.2s\n", container->version, container->subversion); + pr_err("%.8s\n", container->customer); + pr_err("%.8s\n", container->application); + pr_err("%.8s\n", container->type); + pr_err("%d ndev\n", container->ndev); + pr_err("%d nprof\n", container->nprof); + + tfa_err = tfa_load_cnt(container, container_size); + if (tfa_err != tfa_error_ok) { + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return; + } + + /* register codec with dsp */ + tfa98xx->handle = tfa98xx_register_dsp(tfa98xx); + if (tfa98xx->handle < 0) { + dev_err(tfa98xx->dev, "Cannot register with DSP, aborting\n"); + return; + } + + if (tfa_probe(tfa98xx->i2c->addr << 1, &handle) != Tfa98xx_Error_Ok) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return; + } + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->fw.name); + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strcmp(tfaContProfileName(tfa98xx->handle, i), + dflt_prof_name) == 0) { + tfa98xx_profile = i; + dev_info(tfa98xx->dev, + "changing default profile to %s (%d)\n", + dflt_prof_name, tfa98xx_profile); + break; + } + } + if (i >= tfaContMaxProfile(tfa98xx->handle)) + dev_info(tfa98xx->dev, + "Default profile override failed (%s profile not found)\n", + dflt_prof_name); + } + + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK; + pr_err("Firmware init complete\n"); + + if (no_start != 0) + return; + + /* Only controls for master device */ + if (tfa98xx->handle == 0) + tfa98xx_create_controls(tfa98xx); + + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE) { + tfa98xx_interrupt_enable(tfa98xx, true); + return; + } + + mutex_lock(&tfa98xx->dsp_lock); + + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret == Tfa98xx_Error_Ok) + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + mutex_unlock(&tfa98xx->dsp_lock); + tfa98xx_interrupt_enable(tfa98xx, true); +} +#endif +static int tfa98xx_load_container(struct tfa98xx *tfa98xx) +{ + enum tfa_error tfa_err; + int handle; + int container_size; + const struct firmware *fw; + int ret; + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + + ret = request_firmware(&fw,fw_name,tfa98xx->dev); + if(ret < 0) + { + pr_err("Failed to read %s\n", fw_name); + return ret; + } + + pr_err("loaded %s - size: %zu\n", fw_name, + fw ? fw->size : 0); + + container = kzalloc(fw->size, GFP_KERNEL); + if (!container) { + release_firmware(fw); + pr_err("Error allocating memory\n"); + return -ENOMEM; + } + container_size = fw->size; + memcpy(container, fw->data, container_size); + release_firmware(fw); + + pr_err("%.2s%.2s\n", container->version, container->subversion); + pr_err("%.8s\n", container->customer); + pr_err("%.8s\n", container->application); + pr_err("%.8s\n", container->type); + pr_err("%d ndev\n", container->ndev); + pr_err("%d nprof\n", container->nprof); + + tfa_err = tfa_load_cnt(container, container_size); + if (tfa_err != tfa_error_ok) { + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return tfa_err; + } + + /* register codec with dsp */ + tfa98xx->handle = tfa98xx_register_dsp(tfa98xx); + if (tfa98xx->handle < 0) { + dev_err(tfa98xx->dev, "Cannot register with DSP, aborting\n"); + return tfa98xx->handle; + } + + if (tfa_probe(tfa98xx->i2c->addr << 1, &handle) != Tfa98xx_Error_Ok) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return tfa98xx->i2c->addr; + } + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->fw.name); + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + for (i = 0; i < tfaContMaxProfile(tfa98xx->handle); i++) { + if (strcmp(tfaContProfileName(tfa98xx->handle, i), + dflt_prof_name) == 0) { + tfa98xx_profile = i; + dev_info(tfa98xx->dev, + "changing default profile to %s (%d)\n", + dflt_prof_name, tfa98xx_profile); + break; + } + } + if (i >= tfaContMaxProfile(tfa98xx->handle)) + dev_info(tfa98xx->dev, + "Default profile override failed (%s profile not found)\n", + dflt_prof_name); + } + + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK; + pr_err("Firmware init complete\n"); + + if (no_start != 0) + return no_start; + + /* Only controls for master device */ + if (tfa98xx->handle == 0) + tfa98xx_create_controls(tfa98xx); + + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE) { + tfa98xx_interrupt_enable(tfa98xx, true); + return 0; + } + + mutex_lock(&tfa98xx->dsp_lock); + + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret == Tfa98xx_Error_Ok) + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + mutex_unlock(&tfa98xx->dsp_lock); + tfa98xx_interrupt_enable(tfa98xx, true); + //return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + // fw_name, tfa98xx->dev, GFP_KERNEL, + // tfa98xx, tfa98xx_container_loaded); + return 0; +} + + +static void tfa98xx_tapdet(struct tfa98xx *tfa98xx) +{ + unsigned int mem; + int err, btn, count = 0; + uint16_t val; + + /* check tap pattern (BTN_0 is "error" wrong tap indication */ + tfa98xx_dsp_read_mem(tfa98xx->handle, XMEM_TAP_READ, 1, &mem); + switch (mem) { + case 0xffffffff: + pr_info("More than 4 taps detected! (flagTapPattern = -1)\n"); + btn = BTN_0; + break; + case 0xfffffffe: + pr_info("Single tap detected! (flagTapPattern = -2)\n"); + btn = BTN_0; + break; + case 0: + pr_info("Unrecognized pattern! (flagTapPattern = 0)\n"); + btn = BTN_0; + break; + default: + pr_info("Detected pattern: %d\n", mem); + btn = BTN_0 + mem; + break; + } + + input_report_key(tfa98xx->input, btn, 1); + input_report_key(tfa98xx->input, btn, 0); + input_sync(tfa98xx->input); + + /* acknowledge event */ + err = tfa98xx_dsp_write_mem_word(tfa98xx->handle, XMEM_TAP_ACK, 0, Tfa98xx_DMEM_XMEM); + if (err) + pr_info("Unable to write to XMEM\n"); + + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + while ((TFA98XX_STATUSREG_SPKS & val) && (count < 50)) { + val = tfa98xx_read_status_reg(tfa98xx, TFA98XX_STATUSREG_SPKS); + count++; + } + if (count > 1) + pr_info("Had to run %d times to ack SPKS\n", count); + +} + +static void tfa98xx_tapdet_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + u16 val; + + tfa98xx = container_of(work, struct tfa98xx, tapdet_work.work); + + /* Check for SPKS bit*/ + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG); + + if (val & TFA98XX_STATUSREG_SPKS) + tfa98xx_tapdet(tfa98xx); + + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->tapdet_work, HZ/10); +} + +static void tfa98xx_monitor(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + u16 val; + + tfa98xx = container_of(work, struct tfa98xx, monitor_work.work); + + /* Check for tap-detection - bypass monitor if it is active */ + if (!tfa98xx->input) { + /* + * check IC status bits: cold start + * and DSP watch dog bit to re init + */ + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG); + pr_err("SYS_STATUS0: 0x%04x\n", val); + if ((TFA98XX_STATUSREG_ACS & val) || + (TFA98XX_STATUSREG_WDS & val)) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_RECOVER; + + if (TFA98XX_STATUSREG_ACS & val) + pr_err("ERROR: ACS\n"); + if (TFA98XX_STATUSREG_WDS & val) + pr_err("ERROR: WDS\n"); + + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->init_work, 0); + } + + if (TFA98XX_STATUSREG_SPKS & val) + pr_err("ERROR: SPKS\n"); + if (!(TFA98XX_STATUSREG_SWS & val)) + pr_err("ERROR: SWS\n"); + + /* Check secondary errors */ + if ( !(val & TFA98XX_STATUSREG_CLKS) || + !(val & TFA98XX_STATUSREG_UVDS) || + !(val & TFA98XX_STATUSREG_OVDS) || + !(val & TFA98XX_STATUSREG_OTDS) || + !(val & TFA98XX_STATUSREG_PLLS) || + !(val & TFA98XX_STATUSREG_VDDS)) + pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val); + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) { + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUS_FLAGS1); + if ((val & TFA98XX_STATUS_FLAGS1_TDMERR) | + (val & (0x6 << TFA98XX_STATUS_FLAGS1_TDMSTAT_POS)) | + (val & TFA98XX_STATUS_FLAGS1_TDMLUTER)) + pr_err("TDM related errors: STATUS_FLAG1 = 0x%x\n", val); + } + } + + /* reschedule */ + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->monitor_work, 5*HZ); +} + +static void tfa98xx_dsp_init(struct tfa98xx *tfa98xx) +{ + int ret; + bool failed = false; + bool reschedule = false; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + pr_err("Skipping tfa_start (no FW: %d)\n", tfa98xx->dsp_fw_state); + return; + } + + if(tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + pr_err("Stream already started, skipping DSP power-on\n"); + return; + } + + mutex_lock(&tfa98xx->dsp_lock); + + tfa98xx->dsp_init = TFA98XX_DSP_INIT_PENDING; + + if (tfa98xx->init_count < TF98XX_MAX_DSP_START_TRY_COUNT) { + /* directly try to start DSP */ + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx_profile, tfa98xx_vsteps); + if (ret != Tfa98xx_Error_Ok) { + /* It may fail as we may not have a valid clock at that + * time, so re-schedule and re-try later. + */ + dev_err(&tfa98xx->i2c->dev, + "tfa_start failed! (err %d) - %d\n", + ret, tfa98xx->init_count); + reschedule = true; + } else { + /* Subsystem ready, tfa init complete */ + dev_dbg(&tfa98xx->i2c->dev, + "tfa_start success (%d)\n", + tfa98xx->init_count); + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + /* + * start monitor thread to check IC status bit + * periodically, and re-init IC to recover if + * needed. + */ + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, + 1*HZ); + } + } else { + /* exceeded max number ot start tentatives, cancel start */ + dev_err(&tfa98xx->i2c->dev, + "Failed starting device (%d)\n", + tfa98xx->init_count); + failed = true; + } + if (reschedule) { + /* reschedule this init work for later */ + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, + msecs_to_jiffies(5)); + tfa98xx->init_count++; + } + if (failed) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_FAIL; + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + } + mutex_unlock(&tfa98xx->dsp_lock); + return; +} + + +static void tfa98xx_dsp_init_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, init_work.work); + + /* Only do dsp init for master device */ + if (tfa98xx->handle != 0) + return; + + tfa98xx_dsp_init(tfa98xx); +} + +static void tfa98xx_interrupt(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, interrupt_work.work); + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8; + unsigned int base_addr_icl = TFA_FAM(tfa98xx->handle,ICLVDDS) >> 8; + //unsigned int base_addr_ipo = TFA_FAM(tfa98xx->handle,IPOVDDS) >> 8; + + u32 out1, out2, out3; + + pr_info("\n"); + + regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1); + regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2); + regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3); + + out1 &= handles_local[tfa98xx->handle].interrupt_enable[0]; + out2 &= handles_local[tfa98xx->handle].interrupt_enable[1]; + out3 &= handles_local[tfa98xx->handle].interrupt_enable[2]; + + if (out1) { + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 0, out1); + regmap_write(tfa98xx->regmap, base_addr_inten + 0, + handles_local[tfa98xx->handle].interrupt_enable[0]); + } + + if (out2) { + /* manager wait for source state */ + if (TFA_GET_BF_VALUE(tfa98xx->handle, ISTMWSRC, out2) > 0) { + int manwait1 = TFA_GET_BF(tfa98xx->handle, MANWAIT1); + + if (manwait1 > 0) { + pr_info("entering wait for source state\n"); + tfa98xx->count_wait_for_source_state++; + + /* set AMPC and AMPE to make sure the amp is enabled */ + pr_info("setting AMPC and AMPE to 1 (default) \n"); + TFA_SET_BF(tfa98xx->handle, AMPC, 1); + TFA_SET_BF(tfa98xx->handle, AMPE, 1); + + /* set MANSCONF here, the manager will continue if clock is there */ + TFA_SET_BF(tfa98xx->handle, MANSCONF, 1); + } else { + /* Now we can switch profile with internal clock it is not required to call tfa_start */ + + pr_info("leaving wait for source state\n"); + + TFA_SET_BF(tfa98xx->handle, MANSCONF, 0); + } + + if (manwait1 > 0) + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 0); + else + TFA_SET_BF(tfa98xx->handle, IPOMWSRC, 1); + } + + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 1, out2); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, + handles_local[tfa98xx->handle].interrupt_enable[1]); + } + + if (out3) { + /* clear and enable interrupt(s) again */ + regmap_write(tfa98xx->regmap, base_addr_icl + 2, out3); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, + handles_local[tfa98xx->handle].interrupt_enable[2]); + } + +} + +static int tfa98xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int sr; + int len, prof, nprof = tfaContMaxProfile(tfa98xx->handle), idx = 0; + char *basename; + + /* + * Support CODEC to CODEC links, + * these are called with a NULL runtime pointer. + */ + if (!substream->runtime) + return 0; + + if (no_start != 0) + return 0; + + basename = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!basename) + return -ENOMEM; + + /* copy profile name into basename until the . */ + get_profile_basename(basename, tfaContProfileName(tfa98xx->handle, tfa98xx_profile)); + len = strlen(basename); + + /* loop over all profiles and get the supported samples rate(s) from + * the profiles with the same basename + */ + for (prof = 0; prof < nprof; prof++) { + if (0 == strncmp(basename, tfaContProfileName(tfa98xx->handle, prof), len)) { + /* Check which sample rate is supported with current profile, + * and enforce this. + */ + sr = tfa98xx_get_profile_sr(tfa98xx->handle, prof); + if (!sr) + dev_info(codec->dev, "Unable to identify supported sample rate\n"); + tfa98xx->rate_constraint_list[idx++] = sr; + tfa98xx->rate_constraint.count += 1; + } + } + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &tfa98xx->rate_constraint); +} + +static int tfa98xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec_dai->codec); + + tfa98xx->sysclk = freq; + return 0; +} + +static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(dai->codec); + struct snd_soc_codec *codec = dai->codec; + + pr_err("fmt=0x%x\n", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid Codec master mode\n"); + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_PDM: + break; + default: + dev_err(codec->dev, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + tfa98xx->audio_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int tfa98xx_get_fssel(unsigned int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(rate_to_fssel); i++) { + if (rate_to_fssel[i].rate == rate) { + return rate_to_fssel[i].fssel; + } + } + return -EINVAL; +} + +static int tfa98xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int rate; + int prof_idx; + + /* Supported */ + rate = params_rate(params); + pr_err("Requested rate: %d, sample size: %d, physical size: %d\n", + rate, snd_pcm_format_width(params_format(params)), + snd_pcm_format_physical_width(params_format(params))); + + if (params_channels(params) > 2) { + pr_warn("Unusual number of channels: %d\n", params_channels(params)); + } + + if (no_start != 0) + return 0; + + /* check if samplerate is supported for this mixer profile */ + prof_idx = get_profile_id_for_sr(tfa98xx_mixer_profile, rate); + if (prof_idx < 0) { + pr_err("tfa98xx: invalid sample rate %d.\n", rate); + return -EINVAL; + } + pr_err("mixer profile:container profile = [%d:%d]\n", tfa98xx_mixer_profile, prof_idx); + + + /* update 'real' profile (container profile) */ + tfa98xx_profile = prof_idx; + + /* update to new rate */ + tfa98xx->rate = rate; + + return 0; +} + +static int tfa98xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + dev_dbg(&tfa98xx->i2c->dev, "state: %d\n", mute); + + if (!(tfa98xx->flags & TFA98XX_FLAG_DSP_START_ON_MUTE)) + return 0; + + if (no_start) { + pr_err("no_start parameter set no tfa_start or tfa_stop, returning\n"); + return 0; + } + + if (mute) { + /* stop DSP only when both playback and capture streams + * are deactivated + */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tfa98xx->pstream = 0; + else + tfa98xx->cstream = 0; + if (tfa98xx->pstream != 0 || tfa98xx->cstream != 0) + return 0; + + cancel_delayed_work_sync(&tfa98xx->monitor_work); + + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + return 0; + mutex_lock(&tfa98xx->dsp_lock); + tfa_stop(); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tfa98xx->pstream = 1; + else + tfa98xx->cstream = 1; + + /* Start DSP */ + if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, + 0); + } + + return 0; +} + +static const struct snd_soc_dai_ops tfa98xx_dai_ops = { + .startup = tfa98xx_startup, + .set_fmt = tfa98xx_set_fmt, + .set_sysclk = tfa98xx_set_dai_sysclk, + .hw_params = tfa98xx_hw_params, + .mute_stream = tfa98xx_mute, +}; + +static struct snd_soc_dai_driver tfa98xx_dai[] = { + { + .name = "tfa98xx_codec", + .base = TFA98XX_TDM_CONFIG0 - 1, + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + .ops = &tfa98xx_dai_ops, + .symmetric_rates = 1, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif + }, +}; + +static int tfa98xx_probe(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ret; + + pr_err("\n"); + + tfa98xx->rate_constraint.list = &tfa98xx->rate_constraint_list[0]; + tfa98xx->rate_constraint.count = + ARRAY_SIZE(tfa98xx->rate_constraint_list); + + /* setup work queue, will be used to initial DSP on first boot up */ + tfa98xx->tfa98xx_wq = create_singlethread_workqueue("tfa98xx"); + if (!tfa98xx->tfa98xx_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&tfa98xx->init_work, tfa98xx_dsp_init_work); + INIT_DELAYED_WORK(&tfa98xx->monitor_work, tfa98xx_monitor); + INIT_DELAYED_WORK(&tfa98xx->interrupt_work, tfa98xx_interrupt); + INIT_DELAYED_WORK(&tfa98xx->tapdet_work, tfa98xx_tapdet_work); + + tfa98xx->codec = codec; + + ret = tfa98xx_load_container(tfa98xx); + pr_err("Container loading requested: %d\n", ret); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + codec->control_data = tfa98xx->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } +#endif + tfa98xx_add_widgets(tfa98xx); + + dev_info(codec->dev, "tfa98xx codec registered (%s)", + tfa98xx->fw.name); + + return ret; +} + +static int tfa98xx_remove(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + pr_err("\n"); + + tfa98xx_inputdev_unregister(tfa98xx); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + if (tfa98xx->tfa98xx_wq) + destroy_workqueue(tfa98xx->tfa98xx_wq); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) +struct regmap *tfa98xx_get_regmap(struct device *dev) +{ + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + return tfa98xx->regmap; +} +#endif +static struct snd_soc_codec_driver soc_codec_dev_tfa98xx = { + .probe = tfa98xx_probe, + .remove = tfa98xx_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + .get_regmap = tfa98xx_get_regmap, +#endif +}; + + +static bool tfa98xx_writeable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_readable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_volatile_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static const struct regmap_config tfa98xx_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = TFA98XX_MAX_REGISTER, + .writeable_reg = tfa98xx_writeable_register, + .readable_reg = tfa98xx_readable_register, + .volatile_reg = tfa98xx_volatile_register, + .cache_type = REGCACHE_NONE, +}; + + +static void tfa98xx_irq_tfa2(struct tfa98xx *tfa98xx) +{ + unsigned int base_addr_inten = TFA_FAM(tfa98xx->handle,INTENVDDS) >> 8; + unsigned int base_addr_ist = TFA_FAM(tfa98xx->handle,ISTVDDS) >> 8; + u32 en1, en2, en3; + u32 out1 = 0, out2 = 0, out3 = 0; + + pr_info("\n"); + + regmap_read(tfa98xx->regmap, base_addr_inten + 0, &en1); + regmap_read(tfa98xx->regmap, base_addr_inten + 1, &en2); + regmap_read(tfa98xx->regmap, base_addr_inten + 2, &en3); + + regmap_read(tfa98xx->regmap, base_addr_ist + 0, &out1); + regmap_read(tfa98xx->regmap, base_addr_ist + 1, &out2); + regmap_read(tfa98xx->regmap, base_addr_ist + 2, &out3); + + pr_info("interrupt1: 0x%.4x (enabled: 0x%.4x)\n", out1, en1); + pr_info("interrupt2: 0x%.4x (enabled: 0x%.4x)\n", out2, en2); + pr_info("interrupt3: 0x%.4x (enabled: 0x%.4x)\n", out3, en3); + + out1 &= en1; + out2 &= en2; + out3 &= en3; + + en1 = handles_local[tfa98xx->handle].interrupt_enable[0] ^ out1; + en2 = handles_local[tfa98xx->handle].interrupt_enable[1] ^ out2; + en3 = handles_local[tfa98xx->handle].interrupt_enable[2] ^ out3; + + regmap_write(tfa98xx->regmap, base_addr_inten + 0, en1); + regmap_write(tfa98xx->regmap, base_addr_inten + 1, en2); + regmap_write(tfa98xx->regmap, base_addr_inten + 2, en3); + + if (out1 || out2 || out3) + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->interrupt_work, 0); +} + +static void __tfa98xx_irq(struct tfa98xx *tfa98xx) +{ + uint16_t val; + uint16_t ie = handles_local[tfa98xx->handle].interrupt_status[0]; + + val = snd_soc_read(tfa98xx->codec, TFA98XX_STATUSREG); + + dev_info(&tfa98xx->i2c->dev, "interrupt: 0x%04x (enabled: 0x%04x)\n", val, ie); +#ifdef DEBUG + if (!(val & ie)) { + unsigned int ireg; + /* interrupt triggered while all interrupt sources supposedly + * disabled + */ + ireg = snd_soc_read(tfa98xx->codec, TFA98XX_INTERRUPT_REG); + dev_dbg(&tfa98xx->i2c->dev, "ICR: 0x%04x\n", ireg); + } +#endif + + val &= ie; + + /* Check for SPKS bit */ + if (val & TFA98XX_STATUSREG_SPKS) + tfa98xx_tapdet(tfa98xx); +} + +static irqreturn_t tfa98xx_irq(int irq, void *data) +{ + struct tfa98xx *tfa98xx = data; + + if (tfa98xx_dev_family(tfa98xx->handle) == 2) + tfa98xx_irq_tfa2(tfa98xx); + else + __tfa98xx_irq(tfa98xx); + + return IRQ_HANDLED; +} + +static int tfa98xx_ext_reset(struct tfa98xx *tfa98xx) +{ + if (tfa98xx && gpio_is_valid(tfa98xx->reset_gpio)) { + gpio_set_value_cansleep(tfa98xx->reset_gpio, 1); + gpio_set_value_cansleep(tfa98xx->reset_gpio, 0); + } + return 0; +} + +static int tfa98xx_parse_dt(struct device *dev, struct tfa98xx *tfa98xx, + struct device_node *np) { + tfa98xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (tfa98xx->reset_gpio < 0) + dev_dbg(dev, "No reset GPIO provided, will not HW reset device\n"); + + tfa98xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (tfa98xx->irq_gpio < 0) + dev_dbg(dev, "No IRQ GPIO provided.\n"); + + return 0; +} + +static ssize_t tfa98xx_reg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + if (count != 1) { + pr_err("invalid register address"); + return -EINVAL; + } + + tfa98xx->reg = buf[0]; + + return 1; +} + +static ssize_t tfa98xx_rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + u8 *data; + int ret; + int retries = I2C_RETRIES; + + data = kmalloc(count+1, GFP_KERNEL); + if (data == NULL) { + pr_err("can not allocate memory\n"); + return -ENOMEM; + } + + data[0] = tfa98xx->reg; + memcpy(&data[1], buf, count); + +retry: + ret = i2c_master_send(tfa98xx->i2c, data, count+1); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + kfree(data); + return ret; +} + +static ssize_t tfa98xx_rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + struct i2c_msg msgs[] = { + { + .addr = tfa98xx->i2c->addr, + .flags = 0, + .len = 1, + .buf = &tfa98xx->reg, + }, + { + .addr = tfa98xx->i2c->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }, + }; + int ret; + int retries = I2C_RETRIES; +retry: + ret = i2c_transfer(tfa98xx->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return ret; + } + /* ret contains the number of i2c messages send */ + return 1 + ((ret > 1) ? count : 0); +} + +static struct bin_attribute dev_attr_rw = { + .attr = { + .name = "rw", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = tfa98xx_rw_read, + .write = tfa98xx_rw_write, +}; + +static struct bin_attribute dev_attr_reg = { + .attr = { + .name = "reg", + .mode = S_IWUSR, + }, + .size = 0, + .read = NULL, + .write = tfa98xx_reg_write, +}; + +static int tfa98xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct tfa98xx *tfa98xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags; + unsigned int reg; + int ret; + + pr_info("%s\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + tfa98xx = devm_kzalloc(&i2c->dev, sizeof(struct tfa98xx), + GFP_KERNEL); + if (tfa98xx == NULL) + return -ENOMEM; + + tfa98xx->dev = &i2c->dev; + tfa98xx->i2c = i2c; + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + tfa98xx->rate = 48000; /* init to the default sample rate (48kHz) */ + + tfa98xx->regmap = devm_regmap_init_i2c(i2c, &tfa98xx_regmap); + if (IS_ERR(tfa98xx->regmap)) { + ret = PTR_ERR(tfa98xx->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err; + } + + i2c_set_clientdata(i2c, tfa98xx); + mutex_init(&tfa98xx->dsp_lock); + init_waitqueue_head(&tfa98xx->wq); + + if (np) { + ret = tfa98xx_parse_dt(&i2c->dev, tfa98xx, np); + if (ret) { + dev_err(&i2c->dev, "Failed to parse DT node\n"); + goto err; + } + if (no_start) + tfa98xx->irq_gpio = -1; + } else { + tfa98xx->reset_gpio = -1; + tfa98xx->irq_gpio = -1; + } + + if (gpio_is_valid(tfa98xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "TFA98XX_RST"); + if (ret) + goto err; + } + + if (gpio_is_valid(tfa98xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->irq_gpio, + GPIOF_DIR_IN, "TFA98XX_INT"); + if (ret) + goto err; + } + + /* Power up! */ + tfa98xx_ext_reset(tfa98xx); + + if (no_start == 0) { + ret = regmap_read(tfa98xx->regmap, 0x03, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read Revision register: %d\n", + ret); + return -EIO; + } + switch (reg & 0xff) { + case 0x88: /* tfa9888 */ + pr_info("TFA9888 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_STEREO_DEVICE; + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + break; + case 0x80: /* tfa9890 */ + case 0x81: /* tfa9890 */ + pr_info("TFA9890 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV; + break; + case 0x92: /* tfa9891 */ + pr_info("TFA9891 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SAAM_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_TAPDET_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_TFA9890_FAM_DEV; + break; + case 0x97: + pr_info("TFA9897 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_DSP_START_ON_MUTE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TFA9897_FAM_DEV; + break; + default: + pr_info("Unsupported device revision (0x%x)\n", reg & 0xff); + return -EINVAL; + } + } + + /* Modify the stream names, by appending the i2c device address. + * This is used with multicodec, in order to discriminate the devices. + * Stream names appear in the dai definition and in the stream . + * We create copies of original structures because each device will + * have its own instance of this structure, with its own address. + */ + dai = devm_kzalloc(&i2c->dev, sizeof(tfa98xx_dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + memcpy(dai, tfa98xx_dai, sizeof(tfa98xx_dai)); + + tfa98xx_append_i2c_address(&i2c->dev, + i2c, + NULL, + 0, + dai, + ARRAY_SIZE(tfa98xx_dai)); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_tfa98xx, dai, + ARRAY_SIZE(tfa98xx_dai)); + + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register TFA98xx: %d\n", ret); + goto err_off; + } + + if (gpio_is_valid(tfa98xx->irq_gpio) && + !(tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(tfa98xx->irq_gpio), + NULL, tfa98xx_irq, irq_flags, + "tfa98xx", tfa98xx); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + gpio_to_irq(tfa98xx->irq_gpio), ret); + goto err_off; + } + tfa98xx_interrupt_setup(tfa98xx); + } else { + dev_info(&i2c->dev, "Skipping IRQ registration\n"); + /* disable feature support if gpio was invalid */ + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + } + +#ifdef CONFIG_DEBUG_FS + tfa98xx_debug_init(tfa98xx, i2c); +#endif + /* Register the sysfs files for climax backdoor access */ + ret = device_create_bin_file(&i2c->dev, &dev_attr_rw); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + ret = device_create_bin_file(&i2c->dev, &dev_attr_reg); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + + pr_info("%s Probe completed successfully!\n", __func__); + + return 0; + +err_off: + tfa98xx_unregister_dsp(tfa98xx); +err: + return ret; +} + +static int tfa98xx_i2c_remove(struct i2c_client *i2c) +{ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + pr_err("\n"); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + device_remove_bin_file(&i2c->dev, &dev_attr_reg); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); +#ifdef CONFIG_DEBUG_FS + tfa98xx_debug_remove(tfa98xx); +#endif + + tfa98xx_unregister_dsp(tfa98xx); + + snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(tfa98xx->irq_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->irq_gpio); + if (gpio_is_valid(tfa98xx->reset_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->reset_gpio); + + return 0; +} + +static const struct i2c_device_id tfa98xx_i2c_id[] = { + { "tfa98xx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tfa98xx_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id tfa98xx_dt_match[] = { + { .compatible = "nxp,tfa98xx" }, + { .compatible = "nxp,tfa9890" }, + { .compatible = "nxp,tfa9891" }, + { .compatible = "nxp,tfa9888" }, + { }, +}; +#endif + +static struct i2c_driver tfa98xx_i2c_driver = { + .driver = { + .name = "tfa98xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tfa98xx_dt_match), + }, + .probe = tfa98xx_i2c_probe, + .remove = tfa98xx_i2c_remove, + .id_table = tfa98xx_i2c_id, +}; + +static int trace_level = 0; +module_param(trace_level, int, S_IRUGO); +MODULE_PARM_DESC(trace_level, "TFA98xx debug trace level (0=off, bits:1=verbose,2=regdmesg,3=regftrace)."); +static int __init tfa98xx_i2c_init(void) +{ + int ret = 0; + + pr_info("TFA98XX driver version %s\n", TFA98XX_VERSION); + + /* Enable debug traces */ + tfa_verbose(trace_level); + tfa98xx_kmsg_regs = trace_level & 2; + tfa98xx_ftrace_regs = trace_level & 4; + + ret = i2c_add_driver(&tfa98xx_i2c_driver); + + return ret; +} +module_init(tfa98xx_i2c_init); + + +static void __exit tfa98xx_i2c_exit(void) +{ + i2c_del_driver(&tfa98xx_i2c_driver); + + kfree(container); +} +module_exit(tfa98xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC TFA98XX driver"); +MODULE_LICENSE("GPL"); + + + diff --git a/sound/soc/codecs/tfa9891/tfa98xx_tfafieldnames.h b/sound/soc/codecs/tfa9891/tfa98xx_tfafieldnames.h new file mode 100755 index 000000000000..8e55f39b2045 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa98xx_tfafieldnames.h @@ -0,0 +1,90 @@ + +typedef struct TfaBfName { + unsigned short bfEnum; + char *bfName; +} tfaBfName_t; + +typedef struct TfaIrqName { + unsigned short irqEnum; + char *irqName; +} tfaIrqName_t; + +#include "tfa1_tfafieldnames.h" +#include "tfa2_tfafieldnames_N1C.h" +/* diffs for specific devices */ +#include "tfa9887_tfafieldnames.h" +#include "tfa9890_tfafieldnames.h" +#include "tfa9891_tfafieldnames.h" + +/* missing 'common' defs break the build but unused in TFA1 context */ +#define TFA1_BF_AMPINSEL -1 +#define TFA1_BF_MANSCONF -1 +#define TFA1_BF_MANCOLD -1 +#define TFA1_BF_INTSMUTE -1 +#define TFA1_BF_CFSMR -1 +#define TFA1_BF_CFSML -1 +#define TFA1_BF_DCMCCAPI -1 +#define TFA1_BF_DCMCCSB -1 +#define TFA1_BF_USERDEF -1 +#define TFA1_BF_MANSTATE -1 +#define TFA1_BF_MANOPER -1 +#define TFA1_BF_REFCKSEL -1 +#define TFA1_BF_VOLSEC -1 +#define TFA1_BF_FRACTDEL -1 +#define TFA1_BF_ACKDMG -1 +#define TFA1_BF_SSRIGHTE -1 +#define TFA1_BF_SSLEFTE -1 +#define TFA1_BF_SWPROFIL 0x8045 /*!< profile save */ +#define TFA1_BF_SWVSTEP 0x80a5 /*!< vstep save */ + +/* missing 'common' defs break the build but unused in TFA2 context */ +#define TFA2_BF_CFSM -1 + + +/* MTP access uses registers + * defs are derived from corresponding bitfield names as used in the BF macros + */ +#define MTPKEY2 MTPK /* unlock key2 MTPK */ +#define MTP0 MTPOTC /* MTP data */ +#define MTP_CONTROL CIMTP /* copy i2c to mtp */ + +/* interrupt enable register uses HW name in TFA2 */ +#define TFA2_BF_INTENVDDS TFA2_BF_IEVDDS + + +/* interrupt bit field names of TFA2 and TFA1 do not match */ +#define TFA1_BF_IEACS TFA1_BF_INTENACS +#define TFA1_BF_IPOACS TFA1_BF_INTPOLACS +#define TFA1_BF_ISTACS TFA1_BF_INTOACS +#define TFA1_BF_ISTVDDS TFA1_BF_INTOVDDS +#define TFA1_BF_ICLVDDS TFA1_BF_INTIVDDS +#define TFA1_BF_IPOVDDS TFA1_BF_INTPOLVDDS +#define TFA1_BF_IENOCLK TFA1_BF_INTENNOCLK +#define TFA1_BF_ISTNOCLK TFA1_BF_INTONOCLK +#define TFA1_BF_IPONOCLK TFA1_BF_INTPOLNOCLK + +/* interrupt bit fields not available on TFA1 */ +#define TFA1_BF_IECLKOOR -1 +#define TFA1_BF_ISTCLKOOR -1 +#define TFA1_BF_IEMWSRC -1 +#define TFA1_BF_ISTMWSRC -1 +#define TFA1_BF_IPOMWSRC -1 +#define TFA1_BF_IEMWSMU -1 +#define TFA1_BF_ISTMWSMU -1 +#define TFA1_BF_IPOMWSMU -1 +#define TFA1_BF_IEMWCFC -1 +#define TFA1_BF_ISTMWCFC -1 +#define TFA1_BF_IPOMWCFC -1 +#define TFA1_BF_CLKOOR -1 +#define TFA1_BF_MANWAIT1 -1 +#define TFA1_BF_MANWAIT2 -1 +#define TFA1_BF_MANMUTE -1 +#define TFA1_BF_IPCLKOOR -1 +#define TFA1_BF_ICLCLKOOR -1 +#define TFA1_BF_IPOSWS -1 +#define TFA1_BF_IESWS -1 +#define TFA1_BF_ISTSWS -1 +#define TFA1_BF_IESPKS -1 +#define TFA1_BF_ISTSPKS -1 +#define TFA1_BF_IPOSPKS -1 + diff --git a/sound/soc/codecs/tfa9891/tfa_container.c b/sound/soc/codecs/tfa9891/tfa_container.c new file mode 100755 index 000000000000..9516641359d7 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_container.c @@ -0,0 +1,2122 @@ +/* + *Copyright 2014 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include "tfa_service.h" +#include "tfa_container.h" +#include "config.h" +#include "tfa.h" +#include "tfa_dsp_fw.h" +#include "tfa98xx_tfafieldnames.h" + +/* module globals */ +static int tfa98xx_cnt_verbose; + +static nxpTfaContainer_t *g_cont=NULL; /* container file */ +static int g_devs=-1; // nr of devices TODO use direct access to cont? +static nxpTfaDeviceList_t *g_dev[TFACONT_MAXDEVS]; +static int g_profs[TFACONT_MAXDEVS]; +static int g_liveds[TFACONT_MAXDEVS]; +static nxpTfaProfileList_t *g_prof[TFACONT_MAXDEVS][TFACONT_MAXPROFS]; +static nxpTfaLiveDataList_t *g_lived[TFACONT_MAXDEVS][TFACONT_MAXPROFS]; +static int nxp_tfa_vstep[TFACONT_MAXDEVS]; +static char errorname[] = "!ERROR!"; +static char nonename[] = "NONE"; + +static void cont_get_devs(nxpTfaContainer_t *cont); + +static int float_to_int(uint32_t x) +{ + unsigned e = (0x7F + 31) - ((*(unsigned *) &x & 0x7F800000) >> 23); + unsigned m = 0x80000000 | (*(unsigned *) &x << 8); + return -(int)((m >> e) & -(e < 32)); +} + +/* + * check the container file and set module global +*/ +enum tfa_error tfa_load_cnt(void *cnt, int length) { + nxpTfaContainer_t *cntbuf = (nxpTfaContainer_t *)cnt; + + g_cont = NULL; + + if (length > TFA_MAX_CNT_LENGTH) { + pr_err("incorrect length\n"); + return tfa_error_container; + } + + if (HDR(cntbuf->id[0],cntbuf->id[1]) == 0) { + pr_err("header is 0\n"); + return tfa_error_container; + } + + if ( (HDR(cntbuf->id[0],cntbuf->id[1])) != paramsHdr ) { + pr_err("wrong header type: 0x%02x 0x%02x\n", cntbuf->id[0],cntbuf->id[1]); + return tfa_error_container; + } + + if (cntbuf->size == 0) { + pr_err("data size is 0\n"); + return tfa_error_container; + } + + /* check CRC */ + if ( tfaContCrcCheckContainer(cntbuf)) { + pr_err("CRC error\n"); + return tfa_error_container; + } + + /* check sub version level */ + if ( (cntbuf->subversion[1] == NXPTFA_PM_SUBVERSION) && + (cntbuf->subversion[0] == '0') ) { + g_cont = cntbuf; + cont_get_devs(g_cont); + } else { + pr_err("container sub-version not supported: %c%c\n", + cntbuf->subversion[0], cntbuf->subversion[1]); + return tfa_error_container; + } + + return tfa_error_ok; +} + +void tfa_deinit(void) +{ + g_cont = NULL; + g_devs = -1; +} + +/* + * Set the debug option + */ +void tfa_cnt_verbose(int level) { + tfa98xx_cnt_verbose = level; +} + +/* start count from 1, 0 is invalid */ +void tfaContSetCurrentVstep(int channel, int vstep_idx) { + if( channel%d\n", channel, TFACONT_MAXDEVS-1); +} + +/* start count from 1, 0 is invalid */ +int tfaContGetCurrentVstep(int channel) { + if( channel%d\n", channel, TFACONT_MAXDEVS-1); + return -1; +} + +nxpTfaContainer_t * tfa98xx_get_cnt(void) { + return g_cont; +} + +/* + * Dump the contents of the file header + */ +void tfaContShowHeader(nxpTfaHeader_t *hdr) { + char _id[2]; + + pr_debug("File header\n"); + + _id[1] = hdr->id >> 8; + _id[0] = hdr->id & 0xff; + pr_debug("\tid:%.2s version:%.2s subversion:%.2s\n", _id, + hdr->version, hdr->subversion); + pr_debug("\tsize:%d CRC:0x%08x \n", hdr->size, hdr->CRC); + pr_debug( "\tcustomer:%.8s application:%.8s type:%.8s\n", hdr->customer, + hdr->application, hdr->type); +} + +/* + * return device list dsc from index + */ +nxpTfaDeviceList_t *tfaContGetDevList(nxpTfaContainer_t * cont, int dev_idx) +{ + uint8_t *base = (uint8_t *) cont; + + if ( (dev_idx < 0) || (dev_idx >= cont->ndev)) + return NULL; + + if (cont->index[dev_idx].type != dscDevice) + return NULL; + + base += cont->index[dev_idx].offset; + return (nxpTfaDeviceList_t *) base; +} + +/* + * get the Nth profile for the Nth device + */ +nxpTfaProfileList_t *tfaContGetDevProfList(nxpTfaContainer_t * cont, int devIdx, + int profIdx) +{ + nxpTfaDeviceList_t *dev; + int idx, hit; + uint8_t *base = (uint8_t *) cont; + + dev = tfaContGetDevList(cont, devIdx); + if (dev) { + for (idx = 0, hit = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscProfile) { + if (profIdx == hit++) + return (nxpTfaProfileList_t *) (dev-> + list + [idx]. + offset + + base); + } + } + } + + return NULL; +} + +/* + * get the Nth lifedata for the Nth device + */ +nxpTfaLiveDataList_t *tfaContGetDevLiveDataList(nxpTfaContainer_t * cont, int devIdx, + int lifeDataIdx) +{ + nxpTfaDeviceList_t *dev; + int idx, hit; + uint8_t *base = (uint8_t *) cont; + + dev = tfaContGetDevList(cont, devIdx); + if (dev) { + for (idx = 0, hit = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscLiveData) { + if (lifeDataIdx == hit++) + return (nxpTfaLiveDataList_t *) + (dev->list[idx].offset + base); + } + } + } + + return NULL; +} + +/* + * Get the max volume step associated with Nth profile for the Nth device + */ +int tfacont_get_max_vstep(int dev_idx, int prof_idx) { + nxpTfaVolumeStep2File_t *vp; + struct nxpTfaVolumeStepMax2File *vp3; + int vstep_count = 0; + vp = (nxpTfaVolumeStep2File_t *) tfacont_getfiledata(dev_idx, prof_idx, volstepHdr); + if (vp == NULL) + return 0; + /* check the header type to load different NrOfVStep appropriately */ + if (tfa98xx_dev_family(dev_idx) == 2) { + /* this is actually tfa2, so re-read the buffer*/ + vp3 = (struct nxpTfaVolumeStepMax2File *) + tfacont_getfiledata(dev_idx, prof_idx, volstepHdr); + if ( vp3 ) { + vstep_count = vp3->NrOfVsteps; + } + } else { + /* this is max1*/ + if ( vp ) { + vstep_count = vp->vsteps; + } + } + return vstep_count; +} + +/** + * Get the file contents associated with the device or profile + * Search within the device tree, if not found, search within the profile + * tree. There can only be one type of file within profile or device. + */ +nxpTfaFileDsc_t *tfacont_getfiledata(int dev_idx, int prof_idx, enum nxpTfaHeaderType type) +{ + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + nxpTfaFileDsc_t *file; + nxpTfaHeader_t *hdr; + unsigned int i; + + if( g_cont==0 ) + return NULL; + + dev = tfaContGetDevList(g_cont, dev_idx); + + if( dev==0 ) + return NULL; + + /* process the device list until a file type is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont); + hdr = (nxpTfaHeader_t *)file->data; + /* check for file type */ + if ( hdr->id == type) { + //pr_debug("%s: file found of type %d in device %s \n", __FUNCTION__, type, tfaContDeviceName(devIdx)); + return (nxpTfaFileDsc_t *)&file->data; + } + } + } + + /* File not found in device tree. + * So, look in the profile list until the file type is encountered + */ + prof=tfaContGetDevProfList(g_cont, dev_idx, prof_idx); + for(i=0;ilength;i++) { + if (prof->list[i].type == dscFile) { + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont); + hdr= (nxpTfaHeader_t *)file->data; + /* check for file type */ + if ( hdr->id == type) { + //pr_debug("%s: file found of type %d in profile %s\n", __FUNCTION__, type, tfaContProfileName(devIdx, profIdx)); + return (nxpTfaFileDsc_t *)&file->data; + } + } + } + + if ( tfa98xx_cnt_verbose ) + pr_debug("%s: no file found of type %d\n", __FUNCTION__, type); + + return NULL; +} + +/* + * fill globals + */ +static void cont_get_devs(nxpTfaContainer_t *cont) { + nxpTfaProfileList_t *prof; + nxpTfaLiveDataList_t *liveD; + int i,j; + int count; + + // get nr of devlists+1 + for(i=0 ; i < cont->ndev ; i++) { + g_dev[i] = tfaContGetDevList(cont, i); // cache it + } + + g_devs=cont->ndev; + // walk through devices and get the profile lists + for (i = 0; i < g_devs; i++) { + j=0; + count=0; + while ((prof = tfaContGetDevProfList(cont, i, j)) != NULL) { + count++; + g_prof[i][j++] = prof; + } + g_profs[i] = count; // count the nr of profiles per device + } + + g_devs=cont->ndev; + // walk through devices and get the livedata lists + for (i = 0; i < g_devs; i++) { + j=0; + count=0; + while ((liveD = tfaContGetDevLiveDataList(cont, i, j)) != NULL) { + count++; + g_lived[i][j++] = liveD; + } + g_liveds[i] = count; // count the nr of livedata per device + } +} + +static char nostring[]="Undefined string"; + +//TODO add to API +#define MODULE_BIQUADFILTERBANK 2 +#define BIQUAD_COEFF_SIZE 6 +/* + * write a parameter file to the device + */ +static enum Tfa98xx_Error tfaContWriteVstep(int dev_idx, nxpTfaVolumeStep2File_t *vp, int vstep) +{ + enum Tfa98xx_Error err; + unsigned short vol; + + if (vstep < vp->vsteps) { + /* vol = (unsigned short)(voldB / (-0.5f)); */ + vol = (unsigned short)(-2 * float_to_int(*((uint32_t *)&vp->vstep[vstep].attenuation))); + if (vol > 255) /* restricted to 8 bits */ + vol = 255; + + err = tfa98xx_set_volume_level(dev_idx, vol); + if (err != Tfa98xx_Error_Ok) + return err; + + err = tfa98xx_dsp_write_preset( dev_idx, sizeof(vp->vstep[0].preset), vp->vstep[vstep].preset); + if (err != Tfa98xx_Error_Ok) + return err; + err = tfa_cont_write_filterbank(dev_idx, vp->vstep[vstep].filter); + + } else { + pr_err("Incorrect volume given. The value vstep[%d] >= %d\n", nxp_tfa_vstep[dev_idx] , vp->vsteps); + err = Tfa98xx_Error_Bad_Parameter; + } + + if ( tfa98xx_cnt_verbose ) pr_debug("vstep[%d][%d]\n", dev_idx, vstep); + + return err; +} + +static enum Tfa98xx_Error tfaContWriteVstepMax2(int dev_idx, nxpTfaVolumeStepMax2File_t *vp, int vstep_idx, int vstep_msg_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + struct nxpTfaVolumeStepRegisterInfo *regInfo = {0}; + struct nxpTfaVolumeStepMessageInfo *msgInfo = {0}; + nxpTfaBitfield_t bitF; + int msgLength=0, i, j, size=0, nrMessages, modified=0; + uint8_t cmdid_changed[3]; + + if(vstep_idx >= vp->NrOfVsteps) { + pr_debug("Volumestep %d is not available \n", vstep_idx); + return Tfa98xx_Error_Bad_Parameter; + } + + for(i=0; i<=vstep_idx; i++) { + regInfo = (struct nxpTfaVolumeStepRegisterInfo*)(vp->vstepsBin + size); + msgInfo = (struct nxpTfaVolumeStepMessageInfo*)(vp->vstepsBin+ + (regInfo->NrOfRegisters * sizeof(uint32_t)+sizeof(regInfo->NrOfRegisters)+size)); + + nrMessages = msgInfo->NrOfMessages; + for(j=0; jvstepsBin+ + (regInfo->NrOfRegisters * sizeof(uint32_t)+sizeof(regInfo->NrOfRegisters)+size)); + /* message length */ + msgLength = ( (msgInfo->MessageLength.b[0] << 16) + (msgInfo->MessageLength.b[1] << 8) + msgInfo->MessageLength.b[2]); + if (i == vstep_idx) { + /* If no vstepMsgIndex is passed on, all message needs to be send */ + if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER) || (vstep_msg_idx == j)) { + /* + * The algoparams and mbdrc msg id will be changed to the reset type when SBSL=0 + * if SBSL=1 the msg will remain unchanged. It's up to the tuning engineer to choose the 'without_reset' + * types inside the vstep. In other words: the reset msg is applied during SBSL==0 else it remains unchanged. + */ + if(TFA_GET_BF(dev_idx, SBSL) == 0) { + if(msgInfo->MessageType == 0) { /* If the messagetype(0) is AlgoParams */ + /* Only do this when not set already */ + if(msgInfo->CmdId[2] != SB_PARAM_SET_ALGO_PARAMS) { + cmdid_changed[0] = msgInfo->CmdId[0]; + cmdid_changed[1] = msgInfo->CmdId[1]; + cmdid_changed[2] = SB_PARAM_SET_ALGO_PARAMS; + modified = 1; + } + } else if(msgInfo->MessageType == 2) { /* If the messagetype(2) is MBDrc */ + /* Only do this when not set already */ + if(msgInfo->CmdId[2] != SB_PARAM_SET_MBDRC) { + cmdid_changed[0] = msgInfo->CmdId[0]; + cmdid_changed[1] = msgInfo->CmdId[1]; + cmdid_changed[2] = SB_PARAM_SET_MBDRC; + modified = 1; + } + } + } + /* Messagetype(3) is Smartstudio Info! Dont send this! */ + if(msgInfo->MessageType != 3) { + if(modified == 1) { + if (tfa98xx_cnt_verbose) { + if(cmdid_changed[2] == SB_PARAM_SET_ALGO_PARAMS) + pr_debug("P-ID for SetAlgoParams modified!: "); + else + pr_debug("P-ID for SetMBDrc modified!: "); + + pr_debug("Command-ID used: 0x%02x%02x%02x \n", + cmdid_changed[0], cmdid_changed[1], cmdid_changed[2]); + } + /* Send payload to dsp (Remove 1 from the length for cmdid) */ + err = tfa_dsp_msg_id(dev_idx, (msgLength-1) * 3, (const char *)msgInfo->ParameterData, cmdid_changed); + if (err != Tfa98xx_Error_Ok) + return err; + } else { + /* Send cmdId + payload to dsp */ + err = tfa_dsp_msg(dev_idx, msgLength * 3,(const char *)msgInfo->CmdId); + if (err != Tfa98xx_Error_Ok) + return err; + } + + /* Set back to zero every time */ + modified = 0; + } + } + } + + if(msgInfo->MessageType == 3) { + /* MessageLength is in bytes */ + size += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength) + msgLength; + } else { + /* MessageLength is in words (3 bytes) */ + size += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength) + sizeof(msgInfo->CmdId) + ((msgLength-1) * 3); + } + } + size += sizeof(regInfo->NrOfRegisters) + (regInfo->NrOfRegisters * sizeof(uint32_t)) + sizeof(msgInfo->NrOfMessages); + } + + if (regInfo->NrOfRegisters == 0) { + pr_debug("No registers in selected vstep (%d)!\n", vstep_idx); + return Tfa98xx_Error_Bad_Parameter; + } + + for(i=0; iNrOfRegisters*2; i++) { + /* Byte swap the datasheetname */ + bitF.field = (uint16_t)(regInfo->registerInfo[i]>>8) | (regInfo->registerInfo[i]<<8); + i++; + bitF.value = (uint16_t)regInfo->registerInfo[i]>>8; + err = tfaRunWriteBitfield(dev_idx , bitF); + if (err != Tfa98xx_Error_Ok) + return err; + } + + /* Save the current vstep */ + tfa_set_swvstep(dev_idx, (unsigned short)vstep_idx); + + return err; +} + +/* + * Write DRC message to the dsp + * If needed modify the cmd-id + */ + +enum Tfa98xx_Error tfaContWriteDrcFile(int dev_idx, int size, uint8_t data[]) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + uint8_t cmdid_changed[3], modified = 0; + + if(TFA_GET_BF(dev_idx, SBSL) == 0) { + /* Only do this when not set already */ + if(data[2] != SB_PARAM_SET_MBDRC) { + cmdid_changed[0] = data[0]; + cmdid_changed[1] = data[1]; + cmdid_changed[2] = SB_PARAM_SET_MBDRC; + modified = 1; + + if (tfa98xx_cnt_verbose) { + pr_debug("P-ID for SetMBDrc modified!: "); + pr_debug("Command-ID used: 0x%02x%02x%02x \n", + cmdid_changed[0], cmdid_changed[1], cmdid_changed[2]); + } + } + } + + if(modified == 1) { + /* Send payload to dsp (Remove 3 from the length for cmdid) */ + err = tfa_dsp_msg_id(dev_idx, size-3, (const char *)data, cmdid_changed); + } else { + /* Send cmdId + payload to dsp */ + err = tfa_dsp_msg(dev_idx, size, (const char *)data); + } + + return err; +} + + +/* + * write a parameter file to the device + * The VstepIndex and VstepMsgIndex are only used to write a specific msg from the vstep file. + */ +enum Tfa98xx_Error tfaContWriteFile(int dev_idx, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaHeader_t *hdr = (nxpTfaHeader_t *)file->data; + nxpTfaHeaderType_t type; + int size; + + if ( tfa98xx_cnt_verbose ) { + tfaContShowHeader(hdr); + } + + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case msgHdr: /* generic DSP message */ + size = hdr->size - sizeof(nxpTfaMsgFile_t); + err = tfa_dsp_msg(dev_idx, size, (const char *)((nxpTfaMsgFile_t *)hdr)->data); + break; + case volstepHdr: + if (tfa98xx_dev_family(dev_idx) == 2) { + err = tfaContWriteVstepMax2(dev_idx, (nxpTfaVolumeStepMax2File_t *)hdr, vstep_idx, vstep_msg_idx); + } else { + err = tfaContWriteVstep(dev_idx, (nxpTfaVolumeStep2File_t *)hdr, vstep_idx); + } + + /* If writing the vstep was succesfull, set new current vstep */ + if(err == Tfa98xx_Error_Ok) { + tfaContSetCurrentVstep(dev_idx, vstep_idx); + } + + break; + case speakerHdr: + if (tfa98xx_dev_family(dev_idx) == 2) { + /* Remove header and xml_id */ + size = hdr->size - sizeof(struct nxpTfaSpkHeader) - sizeof(struct nxpTfaFWVer); + + err = tfa_dsp_msg(dev_idx, size, + (const char *)(((nxpTfaSpeakerFile_t *)hdr)->data + (sizeof(struct nxpTfaFWVer)))); + } else { + size = hdr->size - sizeof(nxpTfaSpeakerFile_t); + err = tfa98xx_dsp_write_speaker_parameters( dev_idx, size, + (const unsigned char *)((nxpTfaSpeakerFile_t *)hdr)->data); + } + break; + case presetHdr: + size = hdr->size - sizeof(nxpTfaPreset_t); + err = tfa98xx_dsp_write_preset( dev_idx, size, (const unsigned char *)((nxpTfaPreset_t *)hdr)->data); + break; + case equalizerHdr: + err = tfa_cont_write_filterbank(dev_idx, ((nxpTfaEqualizerFile_t *)hdr)->filter); + break; + case patchHdr: + size = hdr->size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) ((nxpTfaPatch_t *)hdr)->data); + break; + case configHdr: + size = hdr->size - sizeof(nxpTfaConfig_t); + err = tfa98xx_dsp_write_config(dev_idx, size, (const unsigned char *)((nxpTfaConfig_t *)hdr)->data); + break; + case drcHdr: + if(hdr->version[0] == NXPTFA_DR3_VERSION) { + /* Size is total size - hdrsize(36) - xmlversion(3) */ + size = hdr->size - sizeof(nxpTfaDrc2_t); + err = tfaContWriteDrcFile(dev_idx, size, ((nxpTfaDrc2_t *)hdr)->data); + } else { + /* + * The DRC file is split as: + * 36 bytes for generic header (customer, application, and type) + * 127x3 (381) bytes first block contains the device and sample rate + * independent settings + * 127x3 (381) bytes block the device and sample rate specific values. + * The second block can always be recalculated from the first block, + * if vlsCal and the sample rate are known. + */ + //size = hdr->size - sizeof(nxpTfaDrc_t); + size = 381; /* fixed size for first block */ + + //+381 is done to only send the second part of the drc block + err = tfa98xx_dsp_write_drc( dev_idx, size, ((const unsigned char *)((nxpTfaDrc_t *)hdr)->data+381)); + } + break; + case infoHdr: + /* Ignore */ + break; + default: + pr_err("Header is of unknown type: 0x%x\n", type); + return Tfa98xx_Error_Bad_Parameter; + } + + return err; +} + +/** + * get the 1st of this dsc type this devicelist + */ +nxpTfaDescPtr_t *tfa_cnt_get_dsc(nxpTfaContainer_t *cnt, nxpTfaDescriptorType_t type, int dev_idx) +{ + nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx); + nxpTfaDescPtr_t *this; + int i; + + if ( !dev ) { + return NULL; + } + /* process the list until a the type is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == (uint32_t)type ) { + this = (nxpTfaDescPtr_t *)(dev->list[i].offset+(uint8_t *)cnt); + return this; + } + + } + + return NULL; +} + +/** + * get the device type from the patch in this devicelist + * - find the patch file for this devidx + * - return the devid from the patch or 0 if not found + */ +int tfa_cnt_get_devid(nxpTfaContainer_t *cnt, int dev_idx) { + nxpTfaPatch_t *patchfile; + nxpTfaDescPtr_t *patchdsc; + uint8_t *patchheader; + unsigned short devid, checkaddress; + int checkvalue; + + patchdsc = tfa_cnt_get_dsc(cnt, dscPatch, dev_idx); + patchdsc += 2; /* first the filename dsc and filesize, so skip them */ + patchfile = (nxpTfaPatch_t *)patchdsc; + + patchheader = patchfile->data; + + checkaddress = (patchheader[1] << 8) + patchheader[2]; + checkvalue = + (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5]; + + devid = patchheader[0]; + + if(checkaddress == 0xFFFF && checkvalue != 0xFFFFFF && checkvalue != 0) { + devid = patchheader[5]<<8 | patchheader[0]; /* full revid */ + } + + return devid; +} + +/* + * get the slave for the device if it exists + */ +enum Tfa98xx_Error tfaContGetSlave(int dev_idx, uint8_t *slave_addr) { + nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx); + + if (dev==0 ) { + return Tfa98xx_Error_Bad_Parameter; + } + + *slave_addr = dev->dev; + return Tfa98xx_Error_Ok; +} + +/* + * write a bit field + */ +enum Tfa98xx_Error tfaRunWriteBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t bf) { + enum Tfa98xx_Error error; + uint16_t value; + union { + uint16_t field; + nxpTfaBfEnum_t Enum; + } bfUni; + + value=bf.value; + bfUni.field = bf.field; +#ifdef TFA_DEBUG + if ( tfa98xx_cnt_verbose ) + pr_debug("bitfield: %s=%d (0x%x[%d..%d]=0x%x)\n", tfaContBfName(bfUni.field, tfa98xx_dev_revision(dev_idx)), value, + bfUni.Enum.address, bfUni.Enum.pos, bfUni.Enum.pos+bfUni.Enum.len, value); +#endif + error = tfa_set_bf(dev_idx, bfUni.field, value); + + return error; +} + +/* + * read a bit field + */ +enum Tfa98xx_Error tfaRunReadBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t *bf) { + enum Tfa98xx_Error error; + union { + uint16_t field; + nxpTfaBfEnum_t Enum; + } bfUni; + uint16_t regvalue, msk; + + bfUni.field = bf->field; + + error = tfa98xx_read_register16(dev_idx, (unsigned char)(bfUni.Enum.address), ®value); + if (error) return error; + + msk = ((1<<(bfUni.Enum.len+1))-1)<value = regvalue>>bfUni.Enum.pos; + + return error; +} + +/* + dsp mem direct write + */ +enum Tfa98xx_Error tfaRunWriteDspMem(Tfa98xx_handle_t dev, nxpTfaDspMem_t *cfmem) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int i; + + for(i=0;isize;i++) { + if ( tfa98xx_cnt_verbose ) + pr_debug("dsp mem (%d): 0x%02x=0x%04x\n", cfmem->type, (cfmem->address+i), cfmem->words[i]); + + error = tfa98xx_dsp_write_mem_word(dev, (cfmem->address+i), cfmem->words[i], cfmem->type); + if (error) return error; + } + + return error; +} + +/* + * write filter payload to DSP + * note that the data is in an aligned union for all filter variants + * the aa data is used but it's the same for all of them + */ +enum Tfa98xx_Error tfaRunWriteFilter(Tfa98xx_handle_t dev, nxpTfaContBiquad_t *bq) { + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + enum Tfa98xx_DMEM dmem; + uint16_t address; + uint8_t data[3*3+sizeof(bq->aa.bytes)]; + int i, channel=0, runs=1; + int8_t saved_index=bq->aa.index; /* This is used to set back the index */ + + /* Channel=1 is primary, Channel=2 is secondary*/ + if (bq->aa.index > 100) { + bq->aa.index -= 100; + channel = 2; + } else if (bq->aa.index > 50) { + bq->aa.index -= 50; + channel = 1; + } else if(tfa98xx_dev_family(dev) == 2) { + runs=2; + } + + if ( tfa98xx_cnt_verbose ) { + if(channel == 2) + pr_debug("filter[%d,S]", bq->aa.index); + else if(channel == 1) + pr_debug("filter[%d,P]", bq->aa.index); + else + pr_debug("filter[%d]", bq->aa.index); + } + + for(i=0; iaa.index, &address, channel); + if(dmem<0) + return Tfa98xx_Error_Bad_Parameter; + + /* send a DSP memory message that targets the devices specific memory for the filter + * msg params: which_mem, start_offset, num_words + */ + memset(data, 0, 3*3); + data[2] = dmem; /* output[0] = which_mem */ + data[4] = address >> 8; /* output[1] = start_offset */ + data[5] = address & 0xff; + data[8] = sizeof(bq->aa.bytes)/3; /*output[2] = num_words */ + memcpy( &data[9], bq->aa.bytes, sizeof(bq->aa.bytes)); /* payload */ + + if(tfa98xx_dev_family(dev) == 2) { + error = tfa_dsp_cmd_id_write(dev, MODULE_FRAMEWORK, FW_PAR_ID_SET_MEMORY, sizeof(data), data); + } else { + error = tfa_dsp_cmd_id_write(dev, MODULE_FRAMEWORK, 4 /* param */ , sizeof(data), data); + } + } + +#ifdef TFA_DEBUG + if ( tfa98xx_cnt_verbose ) { + if (bq->aa.index==13) { + pr_debug("=%d,%.0f,%.2f \n", + bq->in.type, bq->in.cutOffFreq, bq->in.leakage); + } else if(bq->aa.index >= 10 && bq->aa.index <= 12) { + pr_debug("=%d,%.0f,%.1f,%.1f \n", bq->aa.type, + bq->aa.cutOffFreq, bq->aa.rippleDb, bq->aa.rolloff); + } else { + pr_debug("= unsupported filter index \n"); + } + } +#endif + + /* Because we can load the same filters multiple times + * For example: When we switch profile we re-write in operating mode. + * We then need to remember the index (primary, secondary or both) + */ + bq->aa.index = saved_index; + + return error; +} + +/* + * write the register based on the input address, value and mask + * only the part that is masked will be updated + */ +enum Tfa98xx_Error tfaRunWriteRegister(Tfa98xx_handle_t handle, nxpTfaRegpatch_t *reg) +{ + enum Tfa98xx_Error error; + uint16_t value,newvalue; + + if ( tfa98xx_cnt_verbose ) + pr_debug("register: 0x%02x=0x%04x (msk=0x%04x)\n", reg->address, reg->value, reg->mask); + + error = tfa98xx_read_register16(handle, reg->address, &value); + if (error) return error; + + value &= ~reg->mask; + newvalue = reg->value & reg->mask; + + value |= newvalue; + error = tfa98xx_write_register16(handle, reg->address, value); + + return error; + +} + +/* + * return the bitfield + */ +nxpTfaBitfield_t tfaContDsc2Bf(nxpTfaDescPtr_t dsc) { + uint32_t *ptr = (uint32_t *) (&dsc); + union { + nxpTfaBitfield_t bf; + uint32_t num; + } num_bf; + + num_bf.num = *ptr; // & TFA_BITFIELDDSCMSK; + + return num_bf.bf; +} + +// write reg and bitfield items in the devicelist to the target +enum Tfa98xx_Error tfaContWriteRegsDev(int dev_idx) { + nxpTfaDeviceList_t *dev = tfaContDevice (dev_idx); + nxpTfaBitfield_t *bitF; + int i; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + + /* process the list until a patch, file of profile is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscPatch || + dev->list[i].type ==dscFile || + dev->list[i].type ==dscProfile ) break; + + if ( dev->list[i].type == dscBitfield) { + bitF = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)g_cont); + err = tfaRunWriteBitfield(dev_idx , *bitF); + } + if ( dev->list[i].type == dscRegister ) { + err = tfaRunWriteRegister( dev_idx, (nxpTfaRegpatch_t *) + ( dev->list[i].offset+(char*)g_cont)); + } + + if ( err ) break; + } + return err; +} + +// write reg and bitfield items in the profilelist the target +enum Tfa98xx_Error tfaContWriteRegsProf(int dev_idx, int prof_idx) { + nxpTfaProfileList_t *prof = tfaContProfile( dev_idx, prof_idx); + nxpTfaBitfield_t *bitf; + unsigned int i; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + if ( tfa98xx_cnt_verbose ) + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(&prof->name), prof_idx); + + /* process the list until the end of the profile or the default section */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section when we switch profile */ + if(prof->list[i].type == dscDefault) + break; + + if ( prof->list[i].type == dscBitfield) { + bitf = (nxpTfaBitfield_t *)( prof->list[i].offset+(uint8_t *)g_cont); + err = tfaRunWriteBitfield(dev_idx , *bitf); + } + if ( prof->list[i].type == dscRegister ) { + err = tfaRunWriteRegister( dev_idx, (nxpTfaRegpatch_t *)( !prof->list[i].offset+g_cont)); + } + if ( err ) break; + } + return err; +} + +// write patchfile in the devicelist to the target +enum Tfa98xx_Error tfaContWritePatch(int dev_idx) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(dev_idx); + nxpTfaFileDsc_t *file; + nxpTfaPatch_t *patchfile; + int size, i; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + /* process the list until a patch is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscPatch ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont); + patchfile =(nxpTfaPatch_t *)&file->data; + if ( tfa98xx_cnt_verbose ) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) patchfile->data); + if ( err ) return err; + } + + } + + return Tfa98xx_Error_Ok; +} + +// write all param files in the devicelist to the target +enum Tfa98xx_Error tfaContWriteFiles(int dev_idx) { + nxpTfaDeviceList_t *dev = tfaContDevice(dev_idx); + nxpTfaFileDsc_t *file; + nxpTfaCmd_t *cmd; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 bytes, and 3 is the msg + int i, size = 0; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + + /* process the list and write all files */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)g_cont); + if ( tfaContWriteFile(dev_idx, file, 0 , TFA_MAX_VSTEP_MSG_MARKER) ){ + return Tfa98xx_Error_Bad_Parameter; + } + } + + if ( dev->list[i].type == dscSetInputSelect || + dev->list[i].type == dscSetOutputSelect || + dev->list[i].type == dscSetProgramConfig || + dev->list[i].type == dscSetLagW || + dev->list[i].type == dscSetGains || + dev->list[i].type == dscSetvBatFactors || + dev->list[i].type == dscSetSensesCal || + dev->list[i].type == dscSetSensesDelay || + dev->list[i].type == dscSetMBDrc ) { + create_dsp_buffer_msg((nxpTfaMsg_t *) + ( dev->list[i].offset+(char*)g_cont), buffer, &size); + err = tfa_dsp_msg(dev_idx, size, buffer); + if ( tfa98xx_cnt_verbose ) { + pr_debug("command: %s=0x%02x%02x%02x \n", + tfaContGetCommandString(dev->list[i].type), + (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); + } + } + + if ( dev->list[i].type == dscCmd ) { + size = *(uint16_t *)(dev->list[i].offset+(char*)g_cont); + err = tfa_dsp_msg(dev_idx, size, dev->list[i].offset+2+(char*)g_cont); + if ( tfa98xx_cnt_verbose ) { + cmd = (nxpTfaCmd_t *)(dev->list[i].offset+(uint8_t *)g_cont); + pr_debug("Writing cmd=0x%02x%02x%02x \n", cmd->value[0], cmd->value[1], cmd->value[2]); + } + } + if (err != Tfa98xx_Error_Ok) + break; + + if ( dev->list[i].type == dscCfMem ) { + err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(dev->list[i].offset+(uint8_t *)g_cont)); + } + + if (err != Tfa98xx_Error_Ok) + break; + } + + return err; +} + +/* + * write all param files in the profilelist to the target + * this is used during startup when maybe ACS is set + */ +enum Tfa98xx_Error tfaContWriteFilesProf(int dev_idx, int prof_idx, int vstep_idx) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx); + unsigned int i; + nxpTfaFileDsc_t *file; + nxpTfaPatch_t *patchfile; + int size; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + /* process the list and write all files */ + for(i=0;ilength;i++) { + switch (prof->list[i].type) { + case dscFile: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont); + err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + break; + case dscFilter: + /* Filters are not written during coldstart + * Since calibration, SBSL and many other actions can overwrite them again + * They are written during operating mode (tfa_start) + */ + //err = tfaRunWriteFilter(dev_idx, (nxpTfaContBiquad_t *)(prof->list[i].offset+(uint8_t *)g_cont)); + break; + case dscPatch: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont); + patchfile =(nxpTfaPatch_t *)&file->data; + if ( tfa98xx_cnt_verbose ) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(dev_idx, size, (const unsigned char *) patchfile->data); + break; + case dscCfMem: + err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(prof->list[i].offset+(uint8_t *)g_cont)); + break; + default: + /* ignore any other type */ + break; + } + } + + return err; +} + +enum Tfa98xx_Error tfaContWriteItem(int dev_idx, nxpTfaDescPtr_t * dsc) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + //nxpTfaFileDsc_t *file; + nxpTfaRegpatch_t *reg; + nxpTfaMode_t *cas; + nxpTfaBitfield_t *bitf; + + switch (dsc->type) { + case dscDefault: + case dscDevice: // ignore + case dscProfile: // profile list + break; + case dscRegister: // register patch + reg = (nxpTfaRegpatch_t *)(dsc->offset+(uint8_t *)g_cont); + return tfaRunWriteRegister(dev_idx, reg); + //pr_debug("$0x%2x=0x%02x,0x%02x\n", reg->address, reg->mask, reg->value); + break; + case dscString: // ascii: zero terminated string + pr_debug(";string: %s\n", tfaContGetString(dsc)); + break; + case dscFile: // filename + file contents + case dscPatch: + break; + case dscMode: + cas = (nxpTfaMode_t *)(dsc->offset+(uint8_t *)g_cont); + if(cas->value == Tfa98xx_Mode_RCV) + tfa98xx_select_mode(dev_idx, Tfa98xx_Mode_RCV); + else + tfa98xx_select_mode(dev_idx, Tfa98xx_Mode_Normal); + break; + case dscCfMem: + err = tfaRunWriteDspMem(dev_idx, (nxpTfaDspMem_t *)(dsc->offset+(uint8_t *)g_cont)); + break; + case dscBitfield: + bitf = (nxpTfaBitfield_t *)(dsc->offset+(uint8_t *)g_cont); + return tfaRunWriteBitfield(dev_idx , *bitf); + break; + case dscFilter: + return tfaRunWriteFilter(dev_idx, (nxpTfaContBiquad_t *)(dsc->offset+(uint8_t *)g_cont)); + break; + } + + return err; +} + +static unsigned int tfa98xx_sr_from_field(unsigned int field) +{ + switch (field) { + case 0: + return 8000; + case 1: + return 11025; + case 2: + return 12000; + case 3: + return 16000; + case 4: + return 22050; + case 5: + return 24000; + case 6: + return 32000; + case 7: + return 44100; + case 8: + return 48000; + default: + return 0; + } +} + +enum Tfa98xx_Error tfa_write_filters(int dev_idx, int prof_idx) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx); + unsigned int i; + int status; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + if ( tfa98xx_cnt_verbose ) { + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(&prof->name), prof_idx); + pr_debug("Waiting for CLKS... \n"); + } + + for(i=10; i>0; i--) { + err = tfa98xx_dsp_system_stable(dev_idx, &status); + if(status) + break; + else + msleep_interruptible(10); + } + + if(i==0) { + if ( tfa98xx_cnt_verbose ) + pr_err("Unable to write filters, CLKS=0 \n"); + + return Tfa98xx_Error_StateTimedOut; + } + + /* process the list until the end of the profile or the default section */ + for(i=0;ilength;i++) { + if ( prof->list[i].type == dscFilter ) { + if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok) + return Tfa98xx_Error_Bad_Parameter; + } + } + + return err; +} + +unsigned int tfa98xx_get_profile_sr(int dev_idx, unsigned int prof_idx) +{ + nxpTfaBitfield_t *bitf; + unsigned int i; + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + int fs_profile = -1; + + dev = tfaContDevice (dev_idx); + if (!dev) + return 0; + + prof = tfaContProfile(dev_idx, prof_idx); + if (!prof) + return 0; + + /* Check profile fields first */ + for(i = 0; i < prof->length; i++) { + if(prof->list[i].type == dscDefault) + break; + + /* check for profile settingd (AUDFS) */ + if ( prof->list[i].type == dscBitfield ) { + bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)g_cont); + if(bitf->field == TFA_FAM(dev_idx, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + } + + pr_debug("%s - profile fs: 0x%x = %dHz (%d - %d)\n", __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + dev_idx, prof_idx); + if (fs_profile != -1) + return tfa98xx_sr_from_field(fs_profile); + + /* Check for container default setting */ + /* process the list until a patch, file of profile is encountered */ + for(i = 0; i < dev->length; i++) { + if ( dev->list[i].type == dscPatch || + dev->list[i].type ==dscFile || + dev->list[i].type ==dscProfile ) break; + + if ( dev->list[i].type == dscBitfield) { + bitf = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)g_cont); + if(bitf->field == TFA_FAM(dev_idx, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + /* Ignore register case */ + } + + pr_debug("%s - default fs: 0x%x = %dHz (%d - %d)\n", __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + dev_idx, prof_idx); + if (fs_profile != -1) + return tfa98xx_sr_from_field(fs_profile); + + return 48000; +} + +enum Tfa98xx_Error get_sample_rate_info(int dev_idx, nxpTfaProfileList_t *prof, nxpTfaProfileList_t *previous_prof, int fs_previous_profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaBitfield_t *bitf; + unsigned int i; + int fs_default_profile=8; /* default is 48kHz */ + int fs_next_profile=8; /* default is 48kHz */ + + + /* ---------- default settings previous profile ---------- */ + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section search for AUDFS */ + if(i < previous_prof->length) { + if ( previous_prof->list[i].type == dscBitfield ) { + bitf = (nxpTfaBitfield_t *)(previous_prof->list[i].offset+(uint8_t *)g_cont); + if(bitf->field == TFA_FAM(dev_idx, AUDFS)) { + fs_default_profile = bitf->value; + break; + } + } + } + } + + /* ---------- settings next profile ---------- */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section */ + if(prof->list[i].type == dscDefault) + break; + /* search for AUDFS */ + if ( prof->list[i].type == dscBitfield ) { + bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)g_cont); + if(bitf->field == TFA_FAM(dev_idx, AUDFS)) { + fs_next_profile = bitf->value; + break; + } + } + } + + /* Enable if needed for debugging! + if ( tfa98xx_cnt_verbose ) { + pr_debug("sample rate from the previous profile: %d \n", fs_previous_profile); + pr_debug("sample rate in the default section: %d \n", fs_default_profile); + pr_debug("sample rate for the next profile: %d \n", fs_next_profile); + } + */ + + if(fs_next_profile != fs_default_profile) { + if ( tfa98xx_cnt_verbose ) + pr_debug("Writing delay tables for AUDFS=%d \n", fs_next_profile); + + /* If the AUDFS from the next profile is not the same as + * the AUDFS from the default we need to write new delay tables + */ + err = tfa98xx_dsp_write_tables(dev_idx, fs_next_profile); + } else if(fs_default_profile != fs_previous_profile) { + if ( tfa98xx_cnt_verbose ) + pr_debug("Writing delay tables for AUDFS=%d \n", fs_default_profile); + + /* But if we do not have a new AUDFS in the next profile and + * the AUDFS from the default profile is not the same as the AUDFS + * from the previous profile we also need to write new delay tables + */ + err = tfa98xx_dsp_write_tables(dev_idx, fs_default_profile); + } + + return err; +} + +/* + * process all items in the profilelist + * NOTE an error return during processing will leave the device muted + * + */ +enum Tfa98xx_Error tfaContWriteProfile(int dev_idx, int prof_idx, int vstep_idx) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx); + nxpTfaProfileList_t *previous_prof = tfaContProfile(dev_idx, tfa_get_swprof(dev_idx)); + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 bytes, and 3 is the msg + unsigned int i, k=0, j=0, tries=0; + nxpTfaFileDsc_t *file; + nxpTfaCmd_t *cmd; + int size = 0, ready, fs_previous_profile = 8; /* default fs is 48kHz*/ + int dev_family = tfa98xx_dev_family(dev_idx); + + if ( !prof || !previous_prof ) { + pr_err("Error trying to get the (previous) swprofile \n"); + return Tfa98xx_Error_Bad_Parameter; + } + + if ( tfa98xx_cnt_verbose ) { + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(dev_idx), + tfaContProfileName(dev_idx,prof_idx),vstep_idx); + } + + /* We only make a power cycle when the profiles are not in the same group */ + if (prof->group == previous_prof->group && prof->group != 0) { + if ( tfa98xx_cnt_verbose ) { + pr_debug("The new profile (%s) is in the same group as the current profile (%s) \n", + tfaContGetString(&prof->name), tfaContGetString(&previous_prof->name)); + } + } else { + /* mute */ + tfaRunMute(dev_idx); + + /* Get current sample rate before we start switching */ + fs_previous_profile = TFA_GET_BF(dev_idx, AUDFS); + + /* clear SBSL to make sure we stay in initCF state */ + if(tfa98xx_dev_family(dev_idx) == 2) { + TFA_SET_BF_VOLATILE(dev_idx, SBSL, 0); + } + + /* When we switch profile we first power down the subsystem + * This should only be done when we are in operating mode + * */ + if (((dev_family == 2) && (TFA_GET_BF(dev_idx, MANSTATE) == 9)) || (dev_family != 2)) { + err = tfa98xx_powerdown(dev_idx, 1); + if (err) return err; + + /* Wait until we are in PLL powerdown */ + do { + err = tfa98xx_dsp_system_stable(dev_idx, &ready); + if (!ready) + break; + else + msleep_interruptible(10); /* wait 10ms to avoid busload */ + tries++; + } while (tries <= 100); + + if (tries > 100) { + pr_debug("Wait for PLL powerdown timed out!\n"); + return Tfa98xx_Error_StateTimedOut; + } + } else { + pr_debug("No need to go to powerdown now \n"); + } + } + + /* set all bitfield settings */ + /* First set all default settings */ + if (tfa98xx_cnt_verbose) { + pr_debug("---------- default settings profile: %s (%d) ---------- \n", + tfaContGetString(&previous_prof->name), tfa_get_swprof(dev_idx)); + + if(tfa98xx_dev_family(dev_idx) == 2) + err = show_current_state(dev_idx); + } + + /* Loop profile length */ + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section try writing the items */ + if(i < previous_prof->length) { + if ( tfaContWriteItem(dev_idx, &previous_prof->list[i]) != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + } + } + + if ( tfa98xx_cnt_verbose ) + pr_debug("---------- new settings profile: %s (%d) ---------- \n", + tfaContGetString(&prof->name), prof_idx); + + /* set new settings */ + for(i=0;ilength;i++) { + /* Remember where we currently are with writing items*/ + j = i; + + /* We only want to write the values before the default section when we switch profile */ + /* process and write all non-file items */ + switch (prof->list[i].type) { + case dscFile: + case dscPatch: + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + case dscCmd: + case dscFilter: + case dscDefault: + /* When one of these files are found, we exit */ + i = prof->length; + break; + default: + err = tfaContWriteItem(dev_idx, &prof->list[i]); + if ( err != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + break; + } + } + + if (prof->group != previous_prof->group || prof->group == 0) { + if(tfa98xx_dev_family(dev_idx) == 2) + TFA_SET_BF_VOLATILE(dev_idx, MANSCONF, 1); + + /* Leave powerdown state */ + err = tfa_cf_powerup(dev_idx); + if (err) return err; + if (tfa98xx_cnt_verbose && tfa98xx_dev_family(dev_idx) == 2) + err = show_current_state(dev_idx); + + if (tfa98xx_dev_family(dev_idx) == 2) { + /* Reset SBSL to 0 (workaround of enbl_powerswitch=0) */ + TFA_SET_BF_VOLATILE(dev_idx, SBSL, 0); + /* Sending commands to DSP we need to make sure RST is 0 (otherwise we get no response)*/ + TFA_SET_BF(dev_idx, RST, 0); + } + } + + /* Check if there are sample rate changes */ + err = get_sample_rate_info(dev_idx, prof, previous_prof, fs_previous_profile); + if (err) return err; + + /* Write files from previous profile (default section) + * Should only be used for the patch&trap patch (file) + */ + if(tfa98xx_dev_family(dev_idx) == 2) { + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section try writing the file */ + if(i < previous_prof->length) { + if(previous_prof->list[i].type == dscFile || previous_prof->list[i].type == dscPatch) { + /* Only write this once */ + if ( tfa98xx_cnt_verbose && k==0) { + pr_debug("---------- files default profile: %s (%d) ---------- \n", + tfaContGetString(&previous_prof->name), prof_idx); + k++; + } + file = (nxpTfaFileDsc_t *)(previous_prof->list[i].offset+(uint8_t *)g_cont); + err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + } + } + } + } + + if ( tfa98xx_cnt_verbose) { + pr_debug("---------- files new profile: %s (%d) ---------- \n", + tfaContGetString(&prof->name), prof_idx); + } + + /* write everything until end or the default section starts + * Start where we currenly left */ + for(i=j;ilength;i++) { + /* We only want to write the values before the default section when we switch profile */ + if(prof->list[i].type == dscDefault) + break; + + switch (prof->list[i].type) { + case dscFile: + case dscPatch: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont); + err = tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + break; + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + create_dsp_buffer_msg((nxpTfaMsg_t *) + ( prof->list[i].offset+(char*)g_cont), buffer, &size); + err = tfa_dsp_msg(dev_idx, size, buffer); + + if ( tfa98xx_cnt_verbose ) + pr_debug("command: %s=0x%02x%02x%02x \n", + tfaContGetCommandString(prof->list[i].type), + (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); + break; + case dscCmd: + size = *(uint16_t *)(prof->list[i].offset+(char*)g_cont); + err = tfa_dsp_msg(dev_idx, size, prof->list[i].offset+2+(char*)g_cont); + + if ( tfa98xx_cnt_verbose ) { + cmd = (nxpTfaCmd_t *)(prof->list[i].offset+(uint8_t *)g_cont); + pr_debug("Writing cmd=0x%02x%02x%02x \n", cmd->value[0], cmd->value[1], cmd->value[2]); + } + break; + default: + /* This allows us to write bitfield, registers or xmem after files */ + if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok) + return Tfa98xx_Error_Bad_Parameter; + break; + } + + if (err != Tfa98xx_Error_Ok) + return err; + } + + if ((prof->group != previous_prof->group || prof->group == 0) && tfa98xx_dev_family(dev_idx) == 2) { + if (TFA_GET_BF(dev_idx, REFCKSEL) == 0) { + /* set SBSL to go to operation mode */ + TFA_SET_BF_VOLATILE(dev_idx, SBSL, 1); + } + } + + return err; +} + +/* + * process only vstep in the profilelist + * + */ +enum Tfa98xx_Error tfaContWriteFilesVstep(int dev_idx, int prof_idx, int vstep_idx) +{ + nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx); + unsigned int i; + nxpTfaFileDsc_t *file; + nxpTfaHeader_t *hdr; + nxpTfaHeaderType_t type; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !prof ) + return Tfa98xx_Error_Bad_Parameter; + + if ( tfa98xx_cnt_verbose ) + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(dev_idx), + tfaContProfileName(dev_idx,prof_idx),vstep_idx); + + /* write vstep file only! */ + for(i=0;ilength;i++) { + if ( prof->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)g_cont); + hdr = (nxpTfaHeader_t *)file->data; + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case volstepHdr: + if ( tfaContWriteFile(dev_idx, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER) ) + return Tfa98xx_Error_Bad_Parameter; + break; + default: + break; + } + } + } + + return err; +} + + +char *tfaContGetString(nxpTfaDescPtr_t * dsc) +{ + if ( dsc->type != dscString) + return nostring; + + return dsc->offset+(char*)g_cont; +} + +void individual_calibration_results(Tfa98xx_handle_t handle) +{ + int value_P, value_S; + + /* Read the calibration result in xmem (529=primary channel) (530=secondary channel) */ + tfa98xx_dsp_read_mem(handle, 529, 1, &value_P); + tfa98xx_dsp_read_mem(handle, 530, 1, &value_S); + + if(value_P != 1 && value_S != 1) + pr_debug("Calibration failed on both channels! \n"); + else if(value_P != 1) { + pr_debug("Calibration failed on Primary (Left) channel! \n"); + TFA_SET_BF_VOLATILE(handle, SSLEFTE, 0); /* Disable the sound for the left speaker */ + } + else if(value_S != 1) { + pr_debug("Calibration failed on Secondary (Right) channel! \n"); + TFA_SET_BF_VOLATILE(handle, SSRIGHTE, 0); /* Disable the sound for the right speaker */ + } + + TFA_SET_BF_VOLATILE(handle, AMPINSEL, 0); /* Set amplifier input to TDM */ + TFA_SET_BF_VOLATILE(handle, SBSL, 1); +} + +char *tfaContGetCommandString(uint32_t type) +{ + if(type == dscSetInputSelect) + return "setInputSelector"; + else if(type == dscSetOutputSelect) + return "setOutputSelector"; + else if(type == dscSetProgramConfig) + return "setProgramConfig"; + else if(type == dscSetLagW) + return "setLagW"; + else if(type == dscSetGains) + return "setGains"; + else if(type == dscSetvBatFactors) + return "setvBatFactors"; + else if(type == dscSetSensesCal) + return "setSensesCal"; + else if(type == dscSetSensesDelay) + return "setSensesDelay"; + else if(type == dscSetMBDrc) + return "setMBDrc"; + else if(type == dscFilter) + return "filter"; + else + return nostring; +} + +/* + * Get the name of the device at a certain index in the container file + * return device name + */ +char *tfaContDeviceName(int dev_idx) { + nxpTfaDeviceList_t *dev; + + if ( dev_idx >= tfa98xx_cnt_max_device() ) + return errorname; + + if ( (dev = tfaContDevice(dev_idx)) == NULL ) + return errorname; + + return tfaContGetString(&dev->name); +} + +/* + * Get the application name from the container file application field + * note that the input stringbuffer should be sizeof(application field)+1 + * + */ +int tfa_cnt_get_app_name(char *name) +{ + unsigned int i; + int len = 0; + + for(i=0; iapplication); i++) { + if (isalnum(g_cont->application[i])) /* copy char if valid */ + name[len++] = g_cont->application[i]; + if (g_cont->application[i]=='\0') { + break; + } + } + name[len++] = '\0'; + + return len; +} + +/* + * Get profile index of the calibration profile. + * Returns: (profile index) if found, (-2) if no + * calibration profile is found or (-1) on error + */ +int tfaContGetCalProfile(int dev_idx) { + int prof, nprof, cal_idx = -2; + + if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) ) + return -1; + + nprof = tfaContMaxProfile(dev_idx); + /* search for the calibration profile in the list of profiles */ + for (prof = 0; prof < nprof; prof++) { + if(strstr(tfaContProfileName(dev_idx, prof), ".cal") != NULL) { + cal_idx = prof; + pr_debug("Using calibration profile: '%s'\n", tfaContProfileName(dev_idx, prof)); + break; + } + } + return cal_idx; +} + +/** + * Is the profile a tap profile ? + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return 1 if the profile is a tap profile or 0 if not + */ +int tfaContIsTapProfile(int dev_idx, int prof_idx) +{ + if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) ) + return -1; + + /* Check if next profile is tap profile */ + if (strstr(tfaContProfileName(dev_idx, prof_idx), ".tap") != NULL) { + pr_debug("Using Tap profile: '%s'\n", tfaContProfileName(dev_idx, prof_idx)); + return 1; + } + + return 0; +} + +/* + * Get the name of the profile at certain index for a device in the container file + * return profile name + */ +char *tfaContProfileName(int dev_idx, int prof_idx) { + nxpTfaProfileList_t *prof; + + if ( (dev_idx < 0) || (dev_idx >= tfa98xx_cnt_max_device()) ) + return errorname; + if ( (prof_idx < 0) || (prof_idx >= tfaContMaxProfile(dev_idx)) ) + return nonename; + + // the Nth profiles for this device + prof=tfaContGetDevProfList(g_cont, dev_idx, prof_idx); + return tfaContGetString(&prof->name); +} + +/* + * return 1st profile list + */ +nxpTfaProfileList_t *tfaContGet1stProfList(nxpTfaContainer_t * cont) +{ + nxpTfaProfileList_t *prof; + uint8_t *b = (uint8_t *) cont; + + int maxdev = 0; + nxpTfaDeviceList_t *dev; + + // get nr of devlists + maxdev = cont->ndev; + // get last devlist + dev = tfaContGetDevList(cont, maxdev - 1); + if(dev == NULL) + return NULL; + // the 1st profile starts after the last device list + b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) + dev->length * (sizeof(nxpTfaDescPtr_t)); + prof = (nxpTfaProfileList_t *) b; + return prof; +} + +/* + * return 1st livedata list + */ +nxpTfaLiveDataList_t *tfaContGet1stLiveDataList(nxpTfaContainer_t * cont) +{ + nxpTfaLiveDataList_t *ldata; + nxpTfaProfileList_t *prof; + nxpTfaDeviceList_t *dev; + uint8_t *b = (uint8_t *) cont; + int maxdev, maxprof; + + // get nr of devlists+1 + maxdev = cont->ndev; + // get nr of proflists + maxprof = cont->nprof; + + // get last devlist + dev = tfaContGetDevList(cont, maxdev - 1); + // the 1st livedata starts after the last device list + b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) + + dev->length * (sizeof(nxpTfaDescPtr_t)); + + while(maxprof != 0) { + // get last proflist + prof = (nxpTfaProfileList_t *) b; + b += sizeof(nxpTfaProfileList_t) + + ((prof->length-1) * (sizeof(nxpTfaDescPtr_t))); + maxprof--; + } + + /* Else the marker falls off */ + b += 4; //bytes + + ldata = (nxpTfaLiveDataList_t *) b; + return ldata; +} + + +enum Tfa98xx_Error tfaContOpen(int dev_idx) +{ + return tfa98xx_open((Tfa98xx_handle_t)dev_idx); +} + +enum Tfa98xx_Error tfaContClose(int dev_idx) +{ + return tfa98xx_close(dev_idx); +} + +/* + * return the device count in the container file + */ +int tfa98xx_cnt_max_device(void) { + return g_cont !=NULL ? g_cont->ndev : 0; +} + +/* + * lookup slave and return device index + */ +int tfa98xx_cnt_slave2idx(int slave_addr) { + int idx; + + for(idx=0;idxdev == slave_addr ) + return idx; + } + + return -1; +} + +/* + * lookup slave and return device revid + */ +int tfa98xx_cnt_slave2revid(int slave_addr) { + int idx = tfa98xx_cnt_slave2idx(slave_addr); + uint16_t revid; + + if (idx<0) + return idx; + + /* note that the device must have been opened before */ + revid = tfa98xx_get_device_revision(idx); + + /* quick check for valid contents */ + return (revid&0xFF) >= 0x12 ? revid : -1 ; +} + +/* + * return the device list pointer + */ +nxpTfaDeviceList_t *tfaContDevice(int dev_idx) { + if(dev_idx < g_devs) + return g_dev[dev_idx]; + //pr_err("Devlist index too high:%d!", idx); + return NULL; +} + +/* + * return the per device profile count + */ +int tfaContMaxProfile(int dev_idx) { + if ( dev_idx >= g_devs) { + //pr_err("Devlist index too high:%d!", ndev); + return 0; + } + return g_profs[dev_idx]; +} + +/* + * return the next profile: + * - assume that all profiles are adjacent + * - calculate the total length of the input + * - the input profile + its length is the next profile + */ +nxpTfaProfileList_t* tfaContNextProfile(nxpTfaProfileList_t* prof) { + uint8_t *this, *next; /* byte pointers for byte pointer arithmetic */ + nxpTfaProfileList_t* nextprof; + int listlength; /* total length of list in bytes */ + + if(prof == NULL) + return NULL; + + if (prof->ID != TFA_PROFID) + return NULL; /* invalid input */ + + this = (uint8_t *)prof; + /* nr of items in the list, length includes name dsc so - 1*/ + listlength = (prof->length - 1)*sizeof(nxpTfaDescPtr_t); + /* the sizeof(nxpTfaProfileList_t) includes the list[0] length */ + next = this + listlength + sizeof(nxpTfaProfileList_t);// - sizeof(nxpTfaDescPtr_t); + nextprof = (nxpTfaProfileList_t *)next; + + if (nextprof->ID != TFA_PROFID) + return NULL; + + return nextprof; +} + +/* + * return the next livedata + */ +nxpTfaLiveDataList_t* tfaContNextLiveData(nxpTfaLiveDataList_t* livedata) { + nxpTfaLiveDataList_t* nextlivedata = (nxpTfaLiveDataList_t *)( (char*)livedata + (livedata->length*4) + + sizeof(nxpTfaLiveDataList_t) -4); + + if (nextlivedata->ID == TFA_LIVEDATAID) + return nextlivedata; + + return NULL; +} + +/* + * return the device list pointer + */ +nxpTfaProfileList_t* tfaContProfile(int dev_idx, int prof_ipx) { + if ( dev_idx >= g_devs) { + //pr_err("Devlist index too high:%d!", ndev); + return NULL; + } + if ( prof_ipx >= g_profs[dev_idx]) { + //pr_err("Proflist index too high:%d!", nprof); + return NULL; + } + + return g_prof[dev_idx][prof_ipx]; +} + +/* + * check CRC for container + * CRC is calculated over the bytes following the CRC field + * + * return non zero value on error + */ +int tfaContCrcCheckContainer(nxpTfaContainer_t *cont) { + uint8_t *base; + size_t size; + uint32_t crc; + + base = (uint8_t *)&cont->CRC + 4; // ptr to bytes following the CRC field + size = (size_t)(cont->size - (base - (uint8_t *)cont)); // nr of bytes following the CRC field + if (size < 0) + return 1; + crc = ~crc32_le(~0u, base, size); + + return crc != cont->CRC; +} + +/** + * Create a buffer which can be used to send to the dsp. + */ +void create_dsp_buffer_msg(nxpTfaMsg_t *msg, char *buffer, int *size) //TODO cleanup +{ + int i, j = 0; + + /* Copy cmdId. Remember that the cmdId is reversed */ + buffer[0] = msg->cmdId[2]; + buffer[1] = msg->cmdId[1]; + buffer[2] = msg->cmdId[0]; + + /* Copy the data to the buffer */ + for(i=3; i<3+(msg->msg_size*3); i++) { + buffer[i] = (uint8_t) ((msg->data[j] >> 16) & 0xffff); + i++; + buffer[i] = (uint8_t) ((msg->data[j] >> 8) & 0xff); + i++; + buffer[i] = (uint8_t) (msg->data[j] & 0xff); + j++; + } + + *size = (3+(msg->msg_size*3)) * sizeof(char); +} + +void get_all_features_from_cnt(Tfa98xx_handle_t dev_idx, int *hw_feature_register, int sw_feature_register[2]) { + nxpTfaFeatures_t *features; + int i; + + nxpTfaDeviceList_t *dev = tfaContDevice(dev_idx); + + /* Init values in case no keyword is defined in cnt file: */ + *hw_feature_register = -1; + sw_feature_register[0] = -1; + sw_feature_register[1] = -1; + + if(dev == NULL) + return; + + // process the device list + for(i=0;ilength;i++) { + if (dev->list[i].type == dscFeatures) { + features = (nxpTfaFeatures_t *)(dev->list[i].offset+(uint8_t *)g_cont); + *hw_feature_register = features->value[0]; + sw_feature_register[0] = features->value[1]; + sw_feature_register[1] = features->value[2]; + break; + } + } +} + +/* wrapper function */ +void get_hw_features_from_cnt(Tfa98xx_handle_t dev_idx, int *hw_feature_register) +{ + int sw_feature_register[2]; + get_all_features_from_cnt(dev_idx, hw_feature_register, sw_feature_register); +} + +/* wrapper function */ +void get_sw_features_from_cnt(Tfa98xx_handle_t dev_idx, int sw_feature_register[2]) +{ + int hw_feature_register; + get_all_features_from_cnt(dev_idx, &hw_feature_register, sw_feature_register); +} + +/* Factory trimming for the Boost converter */ +void tfa_factory_trimmer(Tfa98xx_handle_t dev_idx) +{ + unsigned short currentValue, delta; + int result; + + /* Factory trimming for the Boost converter */ + /* check if there is a correction needed */ + result = TFA_GET_BF(dev_idx, DCMCCAPI); + if (result) { + /* Get currentvalue of DCMCC and the Delta value */ + currentValue = (unsigned short)TFA_GET_BF(dev_idx, DCMCC); + delta = (unsigned short)TFA_GET_BF(dev_idx, USERDEF); + + /* check the sign bit (+/-) */ + result = TFA_GET_BF(dev_idx, DCMCCSB); + if (result == 0) { + /* Do not exceed the maximum value of 15 */ + if(currentValue + delta < 15) { + TFA_SET_BF_VOLATILE(dev_idx, DCMCC, currentValue + delta); + if (tfa98xx_cnt_verbose) + pr_debug("Max coil current is set to: %d \n", currentValue + delta); + } else { + TFA_SET_BF_VOLATILE(dev_idx, DCMCC, 15); + if (tfa98xx_cnt_verbose) + pr_debug("Max coil current is set to: 15 \n"); + } + } else if (result == 1) { + /* Do not exceed the minimum value of 0 */ + if(currentValue - delta > 0) { + TFA_SET_BF_VOLATILE(dev_idx, DCMCC, currentValue - delta); + if (tfa98xx_cnt_verbose) + pr_debug("Max coil current is set to: %d \n", currentValue - delta); + } else { + TFA_SET_BF_VOLATILE(dev_idx, DCMCC, 0); + if (tfa98xx_cnt_verbose) + pr_debug("Max coil current is set to: 0 \n"); + } + } + } +} + +enum Tfa98xx_Error tfa_set_filters(int dev_idx, int prof_idx) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContProfile(dev_idx, prof_idx); + unsigned int i; + + if ( !prof ) + return Tfa98xx_Error_Bad_Parameter; + + /* If we are in powerdown there is no need to set filters */ + if (TFA_GET_BF(dev_idx, PWDN) == 1) + return Tfa98xx_Error_Ok; + + /* loop the profile to find filter settings */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section */ + if(prof->list[i].type == dscDefault) + break; + + /* write all filter settings */ + if ( prof->list[i].type == dscFilter) { + if (tfaContWriteItem(dev_idx, &prof->list[i]) != Tfa98xx_Error_Ok) + return err; + } + } + + return err; +} diff --git a/sound/soc/codecs/tfa9891/tfa_container.h b/sound/soc/codecs/tfa9891/tfa_container.h new file mode 100755 index 000000000000..213b623acddb --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_container.h @@ -0,0 +1,397 @@ +/* + * tfaContainer.h + * + * Created on: Sep 11, 2013 + * Author: wim + */ + +#ifndef TFACONTAINER_H_ +#define TFACONTAINER_H_ + +/* static limits */ +#define TFACONT_MAXDEVS (4) /* maximum nr of devices */ +#define TFACONT_MAXPROFS (16) /* maximum nr of profiles */ + +#include "tfa98xx_parameters.h" + +/** + * Check the container file and set module global + * @param cnt pointer to container file + * @param length the length of the container file + * @return tfa_error + */ +enum tfa_error tfa_load_cnt(void *cnt, int length); + +/** + * Resets init variables + */ +void tfa_deinit(void); + +/** + * Verify the calibration results from each channel + * @param handle the index of the device + */ +void individual_calibration_results(Tfa98xx_handle_t handle); + +/** + * Return the descriptor string + * @param dsc pointer to nxpTfa descriptor + * @return descriptor string + */ +char *tfaContGetString(nxpTfaDescPtr_t *dsc); //TODO + +/** + * Gets the string for the given command type number + * @param type number representing a command + * @return string of a command + */ +char *tfaContGetCommandString(uint32_t type); + +/** + * get the device type from the patch in this devicelist + * - find the patch file for this devidx + * - return the devid from the patch or 0 if not found + * @param cnt pointer to container file + * @param dev_idx device index + * @return descriptor string + */ +int tfa_cnt_get_devid(nxpTfaContainer_t *cnt, int dev_idx) ; + +/** + * Get the number of devices from the container + * @return number of devices + */ +int tfa98xx_cnt_max_device(void); + +/** + * Set verbosity level + * @param level used as boolean + */ +void tfa_cnt_verbose(int level); + +/** + * Return the pointer to the loaded container file + * @return pointer to container, NULL if not loaded. + */ +nxpTfaContainer_t* tfa98xx_get_cnt(void); + +/** + * Lookup slave and return device index + * @param slave_addr address of the slave device + * @return device index + */ +int tfa98xx_cnt_slave2idx(int slave_addr); + +/** + * Lookup slave and return device revid. + * @param slave_addr address of the slave device + * @return device revid + */ +int tfa98xx_cnt_slave2revid(int slave_addr); + +/** + * Get the slave for the device if it exists. + * @param dev_idx the index of the device + * @param slave the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContGetSlave(int dev_idx, uint8_t *slave_addr); + +/** + * Write reg and bitfield items in the devicelist to the target. + * @param device the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsDev(int dev_idx); + +/** + * Write reg and bitfield items in the profilelist to the target. + * @param device the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsProf(int dev_idx, int prof_idx); + +/** + * Write a patchfile in the devicelist to the target. + * @param dev_idx the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWritePatch(int dev_idx); + +/** + * Write all param files in the devicelist to the target. + * @param dev_idx the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFiles(int dev_idx); + +/** + * Get sample rate from passed profile index + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return sample rate value + */ +unsigned int tfa98xx_get_profile_sr(int dev_idx, unsigned int prof_idx); + +/** + * Open the specified device after looking up the target address. + * @param dev_idx the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContOpen(int dev_idx); + +/** + * Close the device. + * @param dev_idx the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContClose(int dev_idx); + +/** + * Get the device name string + * @param dev_idx the index of the device + * @return device name string or error string if not found + */ +char *tfaContDeviceName(int dev_idx); + +/** + * Get the application name from the container file application field + * @param name the input stringbuffer with size: sizeof(application field)+1 + * @return actual string length + */ +int tfa_cnt_get_app_name(char *name); + +/** + * Get profile index of the calibration profile + * @param dev_idx the index of the device + * @return profile index, -2 if no calibration profile is found or -1 on error + */ +int tfaContGetCalProfile(int dev_idx); + +/** + * Is the profile a tap profile ? + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return 1 if the profile is a tap profile or 0 if not + */ +int tfaContIsTapProfile(int dev_idx, int prof_idx); + +/** + * Get the name of the profile at certain index for a device in the container file + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return profile name string or error string if not found + */ +char *tfaContProfileName(int dev_idx, int prof_idx); + +/** + * Get the number of profiles for a device + * @param dev_idx index of the device + * @return the profile count + */ +int tfaContMaxProfile(int dev_idx); + +/** + * Process all items in the profilelist + * NOTE an error return during processing will leave the device muted + * @param dev_idx index of the device + * @param prof_idx index of the profile + * @param vstep_idx index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteProfile(int dev_idx, int prof_idx, int vstep_idx); + +/** + * Specify the speaker configurations (cmd id) (Left, right, both, none) + * @param dev_idx index of the device + * @param configuration name string of the configuration + */ +void tfa98xx_set_spkr_select(Tfa98xx_handle_t dev_idx, char *configuration); + +/** + * Set current vstep for a given channel + * @param vstep_idx index of the vstep + * @param channel index of the channel + */ +void tfaContSetCurrentVstep(int channel, int vstep_idx); + +/** + * Get current vstep for a given channel + * @param channel index of the channel + */ +int tfaContGetCurrentVstep(int channel); + +enum Tfa98xx_Error tfa_cont_write_filterbank(int dev_idx, nxpTfaFilter_t *filter); + +/** + * Write all param files in the profilelist to the target + * this is used during startup when maybe ACS is set + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @param vstep_idx the index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFilesProf(int dev_idx, int prof_idx, int vstep_idx); + + +enum Tfa98xx_Error tfaContWriteFilesVstep(int dev_idx, int prof_idx, int vstep_idx); +enum Tfa98xx_Error tfaContWriteDrcFile(int dev_idx, int size, uint8_t data[]); + +/** + * Get the device list dsc from the tfaContainer + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @return device list pointer + */ +nxpTfaDeviceList_t *tfaContGetDevList(nxpTfaContainer_t * cont, int dev_idx); + +/** + * Get the Nth profile for the Nth device + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return profile list pointer + */ +nxpTfaProfileList_t *tfaContGetDevProfList(nxpTfaContainer_t *cont, int dev_idx, int prof_idx); + +/** + * Get the Nth livedata for the Nth device + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @param livedata_idx the index of the livedata + * @return livedata list pointer + */ +nxpTfaLiveDataList_t *tfaContGetDevLiveDataList(nxpTfaContainer_t *cont, int dev_idx, int livedata_idx); + +/** + * Check CRC for container + * @param cont pointer to the tfaContainer + * @return error value 0 on error + */ +int tfaContCrcCheckContainer(nxpTfaContainer_t *cont); + +/** + * Get the device list pointer + * @param dev_idx the index of the device + * @return pointer to device list + */ +nxpTfaDeviceList_t *tfaContDevice(int dev_idx); + +/** + * Return the pointer to the profile in a list + * @param dev_idx the index of the device + * @param prof_ipx the index of the profile + * @return profile list pointer + */ +nxpTfaProfileList_t* tfaContProfile(int dev_idx, int prof_ipx); + +/** + * Return the pointer to the first profile in a list from the tfaContainer + * @param cont pointer to the tfaContainer + * @return pointer to first profile in profile list + */ +nxpTfaProfileList_t *tfaContGet1stProfList(nxpTfaContainer_t *cont); + +/** + * Return the pointer to the next profile in a list + * @param prof is the pointer to the profile list + * @return profile list pointer + */ +nxpTfaProfileList_t* tfaContNextProfile(nxpTfaProfileList_t *prof); + +/** + * Return the pointer to the first livedata in a list from the tfaContainer + * @param cont pointer to the tfaContainer + * @return pointer to first livedata in profile list + */ +nxpTfaLiveDataList_t *tfaContGet1stLiveDataList(nxpTfaContainer_t *cont); + +/** + * Return the pointer to the next livedata in a list + * @param livedata_idx is the pointer to the livedata list + * @return livedata list pointer + */ +nxpTfaLiveDataList_t* tfaContNextLiveData(nxpTfaLiveDataList_t *livedata_idx); + +/** + * Write a bit field + * @param dev_idx device index + * @param bf bitfield to write + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunWriteBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t bf);//TODO move to run core + +/** + * Write a parameter file to the device + * @param dev_idx device index + * @param file filedescriptor pointer + * @param vstep_idx index to vstep + * @param vstep_msg_idx index to vstep message + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFile(int dev_idx, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx); + +/** + * Get the max volume step associated with Nth profile for the Nth device + * @param dev_idx device index + * @param prof_idx profile index + * @return the number of vsteps + */ +int tfacont_get_max_vstep(int dev_idx, int prof_idx); + +/** + * Get the file contents associated with the device or profile + * Search within the device tree, if not found, search within the profile + * tree. There can only be one type of file within profile or device. + * @param dev_idx I2C device index + * @param prof_idx I2C profile index in the device + * @param type file type + * @return 0 NULL if file type is not found + * @return 1 file contents + */ +nxpTfaFileDsc_t *tfacont_getfiledata(int dev_idx, int prof_idx, enum nxpTfaHeaderType type); + +/** + * Dump the contents of the file header + * @param hdr pointer to file header data + */ +void tfaContShowHeader(nxpTfaHeader_t *hdr); + +/** + * Read a bit field + * @param dev_idx device index + * @param bf bitfield to read out + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunReadBitfield(Tfa98xx_handle_t dev_idx, nxpTfaBitfield_t *bf); + +/** + * Get hw feature bits from container file + * @param dev_idx device index + * @param hw_feature_register pointer to where hw features are stored + */ +void get_hw_features_from_cnt(Tfa98xx_handle_t dev_idx, int *hw_feature_register); + +/** + * Get sw feature bits from container file + * @param dev_idx device index + * @param sw_feature_register pointer to where sw features are stored + */ +void get_sw_features_from_cnt(Tfa98xx_handle_t dev_idx, int sw_feature_register[2]); + +/** + * Factory trimming for the Boost converter + * check if there is a correction needed + * @param dev_idx device index + */ +void tfa_factory_trimmer(Tfa98xx_handle_t dev_idx); + +/** + * Search for filters settings and if found then write them to the device + * @param dev_idx device index + * @param prof_idx profile to look in + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfa_set_filters(int dev_idx, int prof_idx); + +#endif /* TFACONTAINER_H_ */ diff --git a/sound/soc/codecs/tfa9891/tfa_container_crc32.c b/sound/soc/codecs/tfa9891/tfa_container_crc32.c new file mode 100755 index 000000000000..b1ed92fb23b5 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_container_crc32.c @@ -0,0 +1,108 @@ +/* + * tfaContainer_crc32.c + * + * Implement crc32 function + * + */ + +#include "config.h" + +/*- + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + * + * + * CRC32 code derived from work by Gary S. Brown. + */ + +static const uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t crc32_le(uint32_t crc, unsigned char const *buf, size_t len) +{ + const uint8_t *p; + + p = buf; + + while (len--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc; +} diff --git a/sound/soc/codecs/tfa9891/tfa_debug.c b/sound/soc/codecs/tfa9891/tfa_debug.c new file mode 100755 index 000000000000..5f7b00d4efbd --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_debug.c @@ -0,0 +1,302 @@ + +#include "tfa_service.h" +#include "tfa98xx_tfafieldnames.h" +#include "config.h" + +/* support for error code translation into text */ +static char latest_errorstr[64]; + +const char* tfa98xx_get_error_string(enum Tfa98xx_Error error) +{ + const char* pErrStr; + + switch (error) + { + case Tfa98xx_Error_Ok: + pErrStr = "Ok"; + break; + case Tfa98xx_Error_DSP_not_running: + pErrStr = "DSP_not_running"; + break; + case Tfa98xx_Error_Bad_Parameter: + pErrStr = "Bad_Parameter"; + break; + case Tfa98xx_Error_NotOpen: + pErrStr = "NotOpen"; + break; + case Tfa98xx_Error_InUse: + pErrStr = "InUse"; + break; + case Tfa98xx_Error_RpcBusy: + pErrStr = "RpcBusy"; + break; + case Tfa98xx_Error_RpcModId: + pErrStr = "RpcModId"; + break; + case Tfa98xx_Error_RpcParamId: + pErrStr = "RpcParamId"; + break; + case Tfa98xx_Error_RpcInfoId: + pErrStr = "RpcInfoId"; + break; + case Tfa98xx_Error_RpcNotAllowedSpeaker: + pErrStr = "RpcNotAllowedSpeaker"; + break; + case Tfa98xx_Error_Not_Supported: + pErrStr = "Not_Supported"; + break; + case Tfa98xx_Error_I2C_Fatal: + pErrStr = "I2C_Fatal"; + break; + case Tfa98xx_Error_I2C_NonFatal: + pErrStr = "I2C_NonFatal"; + break; + case Tfa98xx_Error_StateTimedOut: + pErrStr = "WaitForState_TimedOut"; + break; + default: + sprintf(latest_errorstr, "Unspecified error (%d)", (int)error); + pErrStr = latest_errorstr; + } + return pErrStr; +} +/*****************************************************************************/ +/* bitfield lookups */ +/* + * generic table lookup functions + */ +/** + * lookup bf in table + * return 'unkown' if not found + */ +static char *tfa_bf2name(tfaBfName_t *table, uint16_t bf) { + int n=0; + + do { + if ((table[n].bfEnum & 0xfff0 ) == (bf & 0xfff0 )) { + return table[n].bfName; + } + } + while( table[n++].bfEnum != 0xffff); + + return table[n-1].bfName; /* last name says unkown */ +} +/** + * lookup name in table + * return 0xffff if not found + */ +static uint16_t tfa_name2bf(tfaBfName_t *table,const char *name) { + int n = 0; + + do { +#if defined(WIN32) || defined(_X64) + if(_stricmp(name, table[n].bfName)==0) + return table[n].bfEnum; +#else + if(strcasecmp(name, table[n].bfName)==0) + return table[n].bfEnum; +#endif + } while (table[n++].bfEnum != 0xffff); + + return 0xffff; +} + +/* + * tfa2 bitfield name table + */ +TFA2_NAMETABLE +TFA2_BITNAMETABLE + +/* + * tfa1 bitfield name tables + */ +TFA1_NAMETABLE +TFA9890_NAMETABLE +TFA9891_NAMETABLE +TFA9887_NAMETABLE +TFA1_BITNAMETABLE +TFA9890_BITNAMETABLE +TFA9891_BITNAMETABLE +TFA9887_BITNAMETABLE +char *tfaContBitName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + switch (rev & 0xff) { + case 0x88: + name = tfa_bf2name(Tfa2BitNames, num); + break; + case 0x97: + name = tfa_bf2name(Tfa1BitNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891BitNames, num); + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa9891BitNames, num);/* try long bitname table */ + break; + case 0x91: + case 0x80: + case 0x81: + name = tfa_bf2name(Tfa9890BitNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1BitNames, num); /* try generic table */ + break; + case 0x12: + name = tfa_bf2name(Tfa9887BitNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1BitNames, num);/* try generic table */ + break; + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + tableLength = sizeof(Tfa1BitNames)/sizeof(tfaBfName_t); /* end of list */ + name = (char *)unknown; + break; + } + return name; +} + +char *tfaContDsName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + switch (rev & 0xff) { + case 0x88: + name = tfa_bf2name(Tfa2DatasheetNames, num); + break; + case 0x97: + name = tfa_bf2name(Tfa1DatasheetNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891DatasheetNames, num); + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa9891BitNames, num);/* try long bitname table */ + break; + case 0x91: + case 0x80: + case 0x81: + name = tfa_bf2name(Tfa9890DatasheetNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1DatasheetNames, num); /* try generic table */ + break; + case 0x12: + name = tfa_bf2name(Tfa9887DatasheetNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1DatasheetNames, num);/* try generic table */ + break; + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); /* end of list */ + name = (char *)unknown; + break; + } + return name; +} + +char *tfaContBfName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + /* if datasheet name does not exist look for bitfieldname */ + name = tfaContDsName(num, rev); + if (strcmp(unknown, name)==0) + name = tfaContBitName(num, rev); + + return name; +} + +uint16_t tfaContBfEnum(const char *name, unsigned short rev) +{ + uint16_t bfnum; + + switch (rev & 0xff) { + case 0x88: + bfnum = tfa_name2bf(Tfa2DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa2BitNames, name);/* try long bitname table */ + break; + case 0x97: + bfnum = tfa_name2bf(Tfa1DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name);/* try generic table */ + break; + case 0x92: + bfnum = tfa_name2bf(Tfa9891DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9891BitNames, name);/* try long bitname table */ + break; + case 0x91: + case 0x80: + case 0x81: + bfnum = tfa_name2bf(Tfa9890DatasheetNames, name); /* my tabel 1st */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1DatasheetNames, name);/* try generic table */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name); /* try 2nd generic table */ + break; + case 0x12: + bfnum = tfa_name2bf(Tfa9887DatasheetNames, name); /* my tabel 1st */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1DatasheetNames, name);/* try generic table */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name);/* try 2nd generic table */ + break; + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + bfnum=0xffff; + break; + } + + return bfnum; +} + +/* + * check all lists for a hit + * this is for the parser to know if it's an existing bitname + */ +uint16_t tfaContBfEnumAny(const char *name) +{ + uint16_t bfnum; + + /* datasheet names first */ + bfnum = tfa_name2bf(Tfa2DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa1DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9891DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9890DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9887DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + /* and then bitfield names */ + bfnum = tfa_name2bf(Tfa2BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa1BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9891BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9890BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9887BitNames, name); + + return bfnum; + +} diff --git a/sound/soc/codecs/tfa9891/tfa_dsp.c b/sound/soc/codecs/tfa9891/tfa_dsp.c new file mode 100755 index 000000000000..113eb7d76686 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_dsp.c @@ -0,0 +1,3298 @@ +/* + *Copyright 2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include "config.h" + +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" +#include "tfa.h" +#include "tfa_service.h" +#include "tfa_container.h" +#include "tfa_dsp_fw.h" +/* TODO: remove genregs usage? */ +#include "tfa98xx_genregs_N1C.h" +/* handle macro for bitfield */ +#define TFA_MK_BF(reg, pos, len) ((reg<<8)|(pos<<4)|(len-1)) + +/* abstract family for register */ +#define FAM_TFA98XX_CF_CONTROLS (TFA_FAM(handle,RST) >> 8) +#define FAM_TFA98XX_CF_MEM (TFA_FAM(handle,MEMA)>> 8) +#define FAM_TFA98XX_MTP0 (TFA_FAM(handle,MTPOTC) >> 8) +#define FAM_TFA98xx_INT_EN (TFA_FAM(handle,INTENVDDS) >> 8) + +#define CF_STATUS_I2C_CMD_ACK 0x01 + +#if (defined(TFA9888) || defined(TFA98XX_FULL)) +void tfa9888_ops(struct tfa_device_ops *ops); +#endif +#if (defined(TFA9891) || defined(TFA98XX_FULL)) +void tfa9891_ops(struct tfa_device_ops *ops); +#endif +#if (defined(TFA9897) || defined(TFA98XX_FULL)) +void tfa9897_ops(struct tfa_device_ops *ops); +#endif +#if (defined(TFA9890) || defined(TFA98XX_FULL)) +void tfa9890_ops(struct tfa_device_ops *ops); +#endif +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) +int tfa9887B_is87(Tfa98xx_handle_t handle); +void tfa9887B_ops(struct tfa_device_ops *ops); +#endif +#if (defined(TFA9887) || defined(TFA98XX_FULL)) +void tfa9887_ops(struct tfa_device_ops *ops); +#endif + +#ifndef MIN +#define MIN(A,B) (A= 0) && (h < MAX_HANDLES)) + retval = handles_local[h].in_use != 0; + + return retval; +} + +int print_calibration(Tfa98xx_handle_t handle, char *str, size_t size) +{ + return snprintf(str, size, " Prim:%d mOhms, Sec:%d mOhms\n", + handles_local[handle].mohm[0], + handles_local[handle].mohm[1]); +} +/*wangdongdong@MultiMedia,2016/11/30,add for calibrate node*/ +int print_calibration_modify(Tfa98xx_handle_t handle, char *str, size_t size) +{ + return snprintf(str, size, "%d:%d\n", + 2, + handles_local[handle].mohm[0]); +} + +/* +* set OTC +*/ +enum Tfa98xx_Error tfa98xxSetCalibrateOnce(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + //unsigned short mtp = 0; + + printk("%s\n",__func__); + err = tfa98xx_set_mtp(handle, 1< MAX_HANDLES) { + _ASSERT(dev_idx >= MAX_HANDLES); + return; + } + + /* invalidate device struct cached values */ + handles_local[dev_idx].hw_feature_bits = -1; + handles_local[dev_idx].sw_feature_bits[0] = -1; + handles_local[dev_idx].sw_feature_bits[1] = -1; + handles_local[dev_idx].profile = -1; + handles_local[dev_idx].vstep[0] = -1; + handles_local[dev_idx].vstep[1] = -1; + /* defaults */ + handles_local[dev_idx].tfa_family = 1; + handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; /* all others */ + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].spkr_select = 0; + handles_local[dev_idx].support_tcoef = supportYes; + handles_local[dev_idx].supportDrc = supportNotSet; + handles_local[dev_idx].support_saam = supportNotSet; + + /* TODO use the getfeatures() for retrieving the features [artf103523] + handles_local[dev_idx].supportDrc = supportNotSet;*/ + + switch (handles_local[dev_idx].rev & 0xff) { + case 0x88: + /* tfa9888 */ + handles_local[dev_idx].tfa_family = 2; + handles_local[dev_idx].spkr_count = 2; + handles_local[dev_idx].daimap = Tfa98xx_DAI_TDM; + tfa9888_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + break; + case 0x97: + /* tfa9897 */ + handles_local[dev_idx].supportDrc = supportNo; + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].daimap = Tfa98xx_DAI_TDM; + tfa9897_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + break; + case 0x92: + /* tfa9891 */ + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + tfa9891_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + break; + case 0x91: + /* tfa9890B */ + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + break; + case 0x80: + case 0x81: + /* tfa9890 */ + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; + handles_local[dev_idx].supportDrc = supportNo; + handles_local[dev_idx].supportFramework = supportNo; + tfa9890_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + break; + case 0x12: + /* tfa9887 / tfa9887B / tfa9895 */ + handles_local[dev_idx].spkr_count = 1; + handles_local[dev_idx].daimap = Tfa98xx_DAI_I2S; + if (tfa9887B_is87(dev_idx)) { + handles_local[dev_idx].support_tcoef = supportNo; + tfa9887_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + } else + tfa9887B_ops(&handles_local[dev_idx].dev_ops); /* register device operations */ + break; + default: + pr_err("unknown device type : 0x%02x\n", handles_local[dev_idx].rev); + _ASSERT(0); + break; + } +} + +/* + * lookup the device type and return the family type + */ +int tfa98xx_dev2family(int dev_type) { + /* only look at the die ID part (lsb byte) */ + switch(dev_type & 0xff) { + case 0x12: + case 0x80: + case 0x81: + case 0x91: + case 0x92: + case 0x97: + return 1; + case 0x88: + return 2; + case 0x50: + return 3; + default: + return 0; + } +} + +int tfa98xx_dev_family(Tfa98xx_handle_t dev_idx) { + return handles_local[dev_idx].tfa_family; +} + +unsigned short tfa98xx_dev_revision(Tfa98xx_handle_t dev_idx) +{ + return handles_local[dev_idx].rev; +} + +void tfa98xx_set_spkr_select(Tfa98xx_handle_t dev_idx, char *configuration) { + char firstLetter; + + /* 4=Left, 2=Right, 1=none, 0=default */ + if (configuration==NULL) + handles_local[dev_idx].spkr_select = 0; + else + { + firstLetter = (char)tolower((unsigned char)configuration[0]); + switch (firstLetter) { + case 'b': /* SC / both -> apply primary also to secondary */ + handles_local[dev_idx].spkr_select = 8; + handles_local[dev_idx].spkr_count = 2; + break; + case 'l': + case 'p': /* DS / left -> only use primary channel */ + handles_local[dev_idx].spkr_select = 4; + handles_local[dev_idx].spkr_count = 1; + break; + case 'r': + case 's': /* DP / right -> only use secondary channel */ + handles_local[dev_idx].spkr_select = 2; + handles_local[dev_idx].spkr_count = 1; + break; + case 'd': /* DC / disable -> skip applying configuration for both */ + handles_local[dev_idx].spkr_select = 1; + handles_local[dev_idx].spkr_count = 2; + break; + default: + handles_local[dev_idx].spkr_select = 0; + handles_local[dev_idx].spkr_count = 2; + break; + } + } +} + +void tfa_mock_probe(int dev_idx, unsigned short revid, int slave_address) +{ + handles_local[dev_idx].slave_address = (unsigned char)slave_address*2; + handles_local[dev_idx].rev = revid; + tfa_set_query_info(dev_idx); +} + +enum Tfa98xx_Error +tfa_soft_probe(int dev_idx, int revid) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = tfaContGetSlave(dev_idx, &handles_local[dev_idx].slave_address); + handles_local[dev_idx].slave_address *=2; + if (error) + return error; + + handles_local[dev_idx].rev = (unsigned short)revid; + tfa_set_query_info(dev_idx); + + return error; +} + +/* + * TODO The slave/cnt check in tfa98xx_register_dsp() should be done here in tfa_probe() + */ +enum Tfa98xx_Error +tfa_probe(unsigned char slave_address, Tfa98xx_handle_t *pHandle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int rev; + int idx; + + _ASSERT(pHandle != NULL); + *pHandle = -1; + + /* when available select index used in container file */ + idx = tfa98xx_cnt_slave2idx(slave_address>>1); + if (idx < 0) + idx = 0; /* when no container file, use first instance */ + + if (handles_local[idx].in_use == 1) + return Tfa98xx_Error_InUse; + + handles_local[idx].in_use = 1; + + switch (slave_address) { + case TFA98XX_GENERIC_SLAVE_ADDRESS: /* same as (0x0E<<1) test adr */ + case 0x68: + case 0x6A: + case 0x6C: + case 0x6E: + case (0x1a<<1): //TODO properly implement foreign i2c addressing like uda1355 + handles_local[idx].buffer_size = NXP_I2C_BufferSize(); + handles_local[idx].slave_address = slave_address; +// TODO: how do we deal with old bugs? +#if (defined(TFA9887) || defined (TFA9887B) || defined(TFA98XX_FULL)) + /* do a dummy read in order to generate + * i2c clocks when accessing the device + * for the first time + */ + rev = TFA_READ_REG(idx, REV); +#endif + /* this can be the very first read, so check error here */ + rev = TFA_READ_REG(idx, REV); + if (rev < 0) /* returns negative if error */ + error = -rev; + if (Tfa98xx_Error_Ok != error) { + handles_local[idx].in_use = 0; + pr_debug("\nError: Unable to read revid from slave:0x%02x \n", slave_address/2); + return error; + } + handles_local[idx].rev = (unsigned short) rev; + *pHandle = idx; + error = Tfa98xx_Error_Ok; +#ifdef __KERNEL__ /* don't spam userspace with information */ + tfa98xx_trace_printk("slave:0x%02x revid:0x%04x\n", slave_address, rev); + pr_debug("slave:0x%02x revid:0x%04x\n", slave_address, rev); +#endif + break; + default: + pr_info("Unknown slave adress! \n"); + /* wrong slave address */ + error = Tfa98xx_Error_Bad_Parameter; + } + + tfa_set_query_info(idx); + + handles_local[idx].in_use = 0; + + return error; +} + +enum Tfa98xx_Error +tfa98xx_open(Tfa98xx_handle_t handle) +{ + if (tfa98xx_handle_is_open(handle)) { + return Tfa98xx_Error_InUse; + } else { + handles_local[handle].in_use = 1; + return Tfa98xx_Error_Ok; + } +} + +/* + * close + */ +enum Tfa98xx_Error tfa98xx_close(Tfa98xx_handle_t handle) +{ + if (tfa98xx_handle_is_open(handle)) { + handles_local[handle].in_use = 0; + return Tfa98xx_Error_Ok; + } else { + return Tfa98xx_Error_NotOpen; + } +} + +/* + * return the target address for the filter on this device + + filter_index: + [0..9] reserved for EQ (not deployed, calc. is available) + [10..12] anti-alias filter + [13] integrator filter + + */ +enum Tfa98xx_DMEM tfa98xx_filter_mem(Tfa98xx_handle_t dev, int filter_index, unsigned short *address, int channel) +{ + enum Tfa98xx_DMEM dmem=-1; + int idx; + unsigned short bq_table[7][4] ={ + /* index: 10, 11, 12, 13 */ + {346,351,356,288}, //87 BRA_MAX_MRA4-2_7.00 + {346,351,356,288}, //90 BRA_MAX_MRA6_9.02 + {467,472,477,409}, //95 BRA_MAX_MRA7_10.02 + {406,411,416,348}, //97 BRA_MAX_MRA9_12.01 + {467,472,477,409}, //91 BRA_MAX_MRAA_13.02 + {8832, 8837, 8842, 8847}, //88 part1 + {8853, 8858, 8863, 8868} //88 part2 + /* Since the 88 is stereo we have 2 parts. + * Every index has 5 values except index 13 this one has 6 values + */ + }; + + if ( (10 <= filter_index) && (filter_index <= 13) ) { + dmem = Tfa98xx_DMEM_YMEM; /* for all devices */ + idx = filter_index-10; + + switch (handles_local[dev].rev & 0xff ) { // only compare lower byte + case 0x12: + if ( tfa9887B_is87(dev) ) + *address = bq_table[0][idx]; + else + *address = bq_table[2][idx]; + break; + case 0x97: + *address = bq_table[3][idx]; + break; + case 0x80: + case 0x81: // for the RAM version + case 0x91: + *address = bq_table[1][idx]; + break; + case 0x92: + *address = bq_table[4][idx]; + break; + case 0x88: + /* Channel 1 = primary, 2 = secondary */ + if(channel == 1) + *address = bq_table[5][idx]; + else + *address = bq_table[6][idx]; + break; + default: + /* unsupported case, possibly intermediate version */ + return -1; + _ASSERT(0); + } + } + return dmem; +} + +/************************ query functions ********************************************************/ +/* no device involved */ +/** + * return revision + */ +void tfa98xx_rev(int *major, int *minor, int *revision) { + *major = TFA98XX_API_REV_MAJOR; + *minor = TFA98XX_API_REV_MINOR; + *revision = TFA98XX_API_REV_REVISION; +} + +/** + * Return the maximum nr of devices (SC39786) + */ +int tfa98xx_max_devices(void) //TODO get from cnt (now only called from contOpen) +{ + return MAX_HANDLES; +} + +/* return the device revision id + */ +unsigned short tfa98xx_get_device_revision(Tfa98xx_handle_t handle) +{ + /* local function. Caller must make sure handle is valid */ + return handles_local[handle].rev; +} + +/** + * return the device digital audio interface (DAI) type bitmap + */ +enum Tfa98xx_DAI tfa98xx_get_device_dai(Tfa98xx_handle_t handle) +{ + /* local function. Caller must make sure handle is valid */ + return handles_local[handle].daimap; +} + +/** + * get the feature bits from the DSP + * - the older tfa9887 does not support this getfeature and + * also no tcoef support so we use this as the info for returning + * this feature + */ +enum Tfa98xx_Error tfa98xx_dsp_support_tcoef(Tfa98xx_handle_t dev_idx, + int *pb_support_tCoef) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + /* if already set return that state , always assume supported */ + if ( handles_local[dev_idx].support_tcoef != supportNotSet) { + *pb_support_tCoef = (handles_local[dev_idx].support_tcoef == supportYes); + } + + handles_local[dev_idx].support_tcoef = *pb_support_tCoef ? supportYes : supportNo; + + return error; +} + +/** + * tfa98xx_supported_speakers + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa98xx_supported_speakers(Tfa98xx_handle_t handle, int* spkr_count) +{ + if (tfa98xx_handle_is_open(handle)) { + *spkr_count = handles_local[handle].spkr_count; + } else + return Tfa98xx_Error_NotOpen; + + return Tfa98xx_Error_Ok; +} + +/** + * tfa98xx_supported_dai + * returns the bitmap of the supported Digital Audio Interfaces + */ +enum Tfa98xx_Error tfa98xx_supported_dai(Tfa98xx_handle_t handle, enum Tfa98xx_DAI *daimap) +{ + if (tfa98xx_handle_is_open(handle)) { + *daimap = handles_local[handle].daimap; + } else + return Tfa98xx_Error_NotOpen; + + return Tfa98xx_Error_Ok; +} + +/* + * tfa98xx_supported_saam + * returns the supportedspeaker as microphone feature + */ +enum Tfa98xx_Error tfa98xx_supported_saam(Tfa98xx_handle_t handle, enum Tfa98xx_saam *saam) +{ + int features; + enum Tfa98xx_Error error; + + if (handles_local[handle].support_saam == supportNotSet) { + error = tfa98xx_dsp_get_hw_feature_bits(handle,&features); + if (error!=Tfa98xx_Error_Ok) + return error; + handles_local[handle].support_saam = + (features & 0x8000)? supportYes : supportNo; /* SAAM is bit15 */ + } + *saam = handles_local[handle].support_saam == supportYes ? Tfa98xx_saam : Tfa98xx_saam_none ; + + return Tfa98xx_Error_Ok; +} + +/* + * tfa98xx_compare_features + * Obtains features_from_MTP and features_from_cnt + */ +enum Tfa98xx_Error tfa98xx_compare_features(Tfa98xx_handle_t handle, int features_from_MTP[3], int features_from_cnt[3]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint32_t value; + uint16_t mtpbf; + unsigned char bytes[3 * 2]; + + //int sw_feature_bits[2]; /* cached feature bits data */ + //int hw_feature_bits; /* cached feature bits data */ + + /* Nothing to test without clock: */ + int status; + tfa98xx_dsp_system_stable(handle, &status); + if (!status) + return Tfa98xx_Error_NoClock; // Only test when we have a clock. + + /* Set proper MTP location per device: */ + if (tfa98xx_dev_family(handle) == 1) { + mtpbf=0x850f; /* MTP5 for tfa1,16 bits */ + } else { + mtpbf=0xf907; /* MTP9 for tfa2, 8 bits */ + } + + /* Read HW features from MTP: */ + value = tfa_read_reg(handle, mtpbf) & 0xffff; + features_from_MTP[0] = handles_local[handle].hw_feature_bits = value; + + /* Read SW features: */ + error = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, + FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes); + if (error != Tfa98xx_Error_Ok) + return error; /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ + tfa98xx_convert_bytes2data(sizeof(bytes), bytes, &features_from_MTP[1]); + + /* check if feature bits from MTP match feature bits from cnt file: */ + get_hw_features_from_cnt(handle, &features_from_cnt[0]); + get_sw_features_from_cnt(handle, &features_from_cnt[1]); + + return error; +} + +/********************************* device specific ops ************************************************/ +/* the wrapper for DspReset, in case of full */ +enum Tfa98xx_Error tfa98xx_dsp_reset(Tfa98xx_handle_t dev_idx, int state) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (tfa98xx_handle_is_open(dev_idx)) { + if (handles_local[dev_idx].dev_ops.tfa_dsp_reset) + error = (*handles_local[dev_idx].dev_ops.tfa_dsp_reset)(dev_idx, state); + else + /* generic function */ + TFA_SET_BF_VOLATILE(dev_idx, RST, (uint16_t)state); + } + return error; +} + +/* tfa98xx_dsp_system_stable + * return: *ready = 1 when clocks are stable to allow DSP subsystem access + */ + +/* This is the clean, default static + */ +static enum Tfa98xx_Error _tfa98xx_dsp_system_stable(Tfa98xx_handle_t handle, + int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short status; + int value; + + /* check the contents of the STATUS register */ + value = TFA_READ_REG(handle, AREFS); + if (value < 0) { + error = -value; + *ready = 0; + _ASSERT(error); /* an error here can be fatal */ + return error; + } + status = (unsigned short)value; + + /* check AREFS and CLKS: not ready if either is clear */ + *ready = !((TFA_GET_BF_VALUE(handle, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(handle, CLKS, status) == 0)); + + return error; +} + +/* deferred calibration */ +void tfa98xx_apply_deferred_calibration(Tfa98xx_handle_t handle) +{ + struct tfa98xx_controls *controls = &(handles_local[handle].dev_ops.controls); + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + unsigned short value; + + if (controls->otc.deferrable && controls->otc.triggered) { + pr_debug("Deferred writing otc = %d\n", controls->otc.wr_value); + err = tfa98xx_set_mtp(handle, + (uint16_t)controls->otc.wr_value << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS, + 1 << TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS); + if (err != Tfa98xx_Error_Ok) { + pr_err("Unable to apply deferred MTP OTC write. Error=%d\n", + err); + } else { + controls->otc.triggered = false; + controls->otc.rd_valid = true; + err = tfa98xx_get_mtp(handle, &value); + if (err == Tfa98xx_Error_Ok) + controls->otc.rd_value = + (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS; + else + controls->otc.rd_value = controls->otc.wr_value; + } + } + + if (controls->mtpex.deferrable && controls->mtpex.triggered) { + pr_debug("Deferred writing mtpex = %d\n", controls->mtpex.wr_value); + err = tfa98xx_set_mtp(handle, + (uint16_t)controls->mtpex.wr_value << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS, + 1 << TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS); + if (err != Tfa98xx_Error_Ok) { + pr_err("Unable to apply deferred MTPEX write. Rrror=%d\n", + err); + } else { + controls->mtpex.triggered = false; + controls->mtpex.rd_valid = true; + err = tfa98xx_get_mtp(handle, &value); + if (err == Tfa98xx_Error_Ok) + controls->mtpex.rd_value = + (value & TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK) + >> TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS; + else + controls->mtpex.rd_value = controls->mtpex.wr_value; + } + } + + if (controls->calib.triggered) { + err = tfa_calibrate(handle); + if(err) { + pr_info("Deferred calibration failed: %d\n", err); + } else { + pr_debug("Deferred calibration ok\n"); + controls->calib.triggered = false; + } + } +} + +/* the ops wrapper for tfa98xx_dsp_SystemStable */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(Tfa98xx_handle_t dev_idx, int *ready) +{ + enum Tfa98xx_Error error; + + if (!tfa98xx_handle_is_open(dev_idx)) + return Tfa98xx_Error_NotOpen; + + if (handles_local[dev_idx].dev_ops.tfa_dsp_system_stable) + error = (*handles_local[dev_idx].dev_ops.tfa_dsp_system_stable)(dev_idx, ready); + else + /* generic function */ + error = _tfa98xx_dsp_system_stable(dev_idx, ready); + return error; +} + +/* the ops wrapper for tfa98xx_dsp_SystemStable */ +int tfa98xx_cf_enabled(Tfa98xx_handle_t dev_idx) +{ + if (!tfa98xx_handle_is_open(dev_idx)) + return Tfa98xx_Error_NotOpen; + + return TFA_GET_BF(dev_idx, CFE); +} + + +/* + * bring the device into a state similar to reset + */ +enum Tfa98xx_Error tfa98xx_init(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t value=0; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + /* reset all i2C registers to default + * Write the register directly to avoid the read in the bitfield function. + * The I2CR bit may overwrite the full register because it is reset anyway. + * This will save a reg read transaction. + */ + TFA_SET_BF_VALUE(handle, I2CR, 1, &value ); + TFA_WRITE_REG(handle, I2CR, value); + + if (tfa98xx_dev_family(handle) == 2) { + /* restore MANSCONF and MANCOLD to POR state */ + TFA_SET_BF_VOLATILE(handle, MANSCONF, 0); + TFA_SET_BF_VOLATILE(handle, MANCOLD, 1); + } else { + /* Mark TFA1 family chips OTC and MTPEX calibration accesses + * as deferrable, since these registers cannot be accesed + * while the I2S link is not up and running + */ + handles_local[handle].dev_ops.controls.otc.deferrable = true; + handles_local[handle].dev_ops.controls.mtpex.deferrable = true; + } + switch (TFA_GET_BF(handle, REV) & 0xff) + { + case 0x80: + pr_debug("tfa98xx_init Dev ID %x\n",(TFA_GET_BF(handle, REV) & 0xff)); + break; + default: + tfa98xx_dsp_reset(handle, 1); /* in pair of tfaRunStartDSP() */ + } + + /* some other registers must be set for optimal amplifier behaviour + * This is implemented in a file specific for the type number + */ + + if (handles_local[handle].dev_ops.tfa_init) + error = (*handles_local[handle].dev_ops.tfa_init)(handle); + //TODO warning here? + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_write_tables(Tfa98xx_handle_t handle, int sample_rate) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + if (handles_local[handle].dev_ops.tfa_dsp_write_tables) + error = (*handles_local[handle].dev_ops.tfa_dsp_write_tables)(handle, sample_rate); + + return error; +} + +/********************* new tfa2 *********************************************************************/ +/* newly added messaging for tfa2 tfa1? */ +enum Tfa98xx_Error tfa98xx_dsp_get_memory(Tfa98xx_handle_t handle, int memoryType, + int offset, int length, unsigned char bytes[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + char msg[12]; + + msg[0] = 8; + msg[1] = MODULE_FRAMEWORK + 128; + msg[2] = FW_PAR_ID_GET_MEMORY; + + msg[3] = 0; + msg[4] = 0; + msg[5] = (char)memoryType; + + msg[6] = 0; + msg[7] = (offset>>8) & 0xff; + msg[8] = offset & 0xff; + + msg[9] = 0; + msg[10] = (length>>8) & 0xff; + msg[11] = length & 0xff; + + /* send msg */ + error = tfa_dsp_msg(handle, sizeof(msg), (char *)msg); + + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the device (length * 3 = words) */ + error = tfa_dsp_msg_read(handle, length * 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_set_memory(Tfa98xx_handle_t handle, int memoryType, + int offset, int length, int value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + char msg[15]; + + msg[0] = 8; + msg[1] = MODULE_FRAMEWORK + 128; + msg[2] = FW_PAR_ID_SET_MEMORY; + + msg[3] = 0; + msg[4] = 0; + msg[5] = (char)memoryType; + + msg[6] = 0; + msg[7] = (offset>>8) & 0xff; + msg[8] = offset & 0xff; + + msg[9] = 0; + msg[10] = (length>>8) & 0xff; + msg[11] = length & 0xff; + + msg[12] = (value>>16) & 0xff; + msg[13] = (value>>8) & 0xff; + msg[14] = value & 0xff; + + /* send msg */ + error = tfa_dsp_msg(handle, sizeof(msg), (char *)msg); + + return error; +} +/****************************** calibration support **************************/ +/* + * get/set the mtp with user controllable values + * + * check if the relevant clocks are available + */ +enum Tfa98xx_Error tfa98xx_get_mtp(Tfa98xx_handle_t handle, uint16_t *value) +{ + int status; + int result; + + /* not possible if PLL in powerdown */ + if ( TFA_GET_BF(handle, PWDN) ) { + pr_debug("PLL in powerdown\n"); + return Tfa98xx_Error_NoClock; + } + + /* tfa1 needs audio PLL */ + if (tfa98xx_dev_family(handle) == 1) { + tfa98xx_dsp_system_stable(handle, &status); + if (status==0) { + pr_debug("PLL not running\n"); + return Tfa98xx_Error_NoClock; + } + } + + result = TFA_READ_REG(handle, MTP0); + if (result < 0) { + return -result; + } + *value = (uint16_t)result; + + return Tfa98xx_Error_Ok; +} + +/* + * lock or unlock KEY2 + * lock = 1 will lock + * lock = 0 will unlock + * + * note that on return all the hidden key will be off + */ +void tfa98xx_key2(Tfa98xx_handle_t handle, int lock) { + + /* unhide lock registers */ + tfa98xx_write_register16(handle, + (tfa98xx_dev_family(handle) == 1) ? 0x40 :0x0F, 0x5A6B); + /* lock/unlock key2 MTPK */ + TFA_WRITE_REG(handle, MTPKEY2, lock? 0 :0x5A ); + /* unhide lock registers */ + tfa98xx_write_register16(handle, + (tfa98xx_dev_family(handle) == 1) ? 0x40 :0x0F, 0); + + +} + +enum Tfa98xx_Error tfa98xx_set_mtp(Tfa98xx_handle_t handle, uint16_t value, + uint16_t mask) +{ + unsigned short mtp_old, mtp_new; + int loop, status; + enum Tfa98xx_Error error; + + error = tfa98xx_get_mtp(handle, &mtp_old); + + if (error != Tfa98xx_Error_Ok) + return error; + + mtp_new = (value & mask) | (mtp_old & ~mask); + + if ( mtp_old == mtp_new) /* no change */ + return Tfa98xx_Error_Ok; + + /* assure that the clock is up, else we can't write MTP */ + error = tfa98xx_dsp_system_stable(handle, &status); + if (error) + return error; + if (status==0) + return Tfa98xx_Error_NoClock; + + tfa98xx_key2(handle , 0); /* unlock */ + TFA_WRITE_REG(handle, MTP0, mtp_new); /* write to i2c shadow reg */ + /* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/ + TFA_SET_BF(handle, CIMTP, 1); + /* no check for MTPBUSY here, i2c delay assumed to be enough */ + tfa98xx_key2(handle , 1); /* lock */ + + /* wait until MTP write is done + */ + for(loop=0; loop<100 /*x10ms*/ ;loop++) { + msleep_interruptible(10); /* wait 10ms to avoid busload */ + if (TFA_GET_BF(handle, MTPB) == 0) + return Tfa98xx_Error_Ok; + } + + return Tfa98xx_Error_StateTimedOut; +} +/* + * clear mtpex + * set ACS + * start tfa + */ +int tfa_calibrate(Tfa98xx_handle_t handle) { + enum Tfa98xx_Error error; + + /* clear mtpex */ + error = tfa98xx_set_mtp(handle, 0, 0x2); + if (error) + return error ; + + /* set ACS/coldboot state */ + error = tfaRunColdboot(handle, 1); + + /* start tfa by playing */ + return error; +} + +static short twos(short x) +{ + return (x<0)? x+512 : x; +} +void tfa98xx_set_exttemp(Tfa98xx_handle_t handle, short ext_temp) +{ + if ((-256 <= ext_temp) && (ext_temp <= 255)) { + /* make twos complement */ + pr_debug("Using ext temp %d C\n", twos(ext_temp)); + TFA_SET_BF(handle, TROS, 1); + TFA_SET_BF(handle, EXTTS, twos(ext_temp)); + } else { + pr_debug("Clearing ext temp settings\n"); + TFA_SET_BF(handle, TROS, 0); + } +} +short tfa98xx_get_exttemp(Tfa98xx_handle_t handle) +{ + short ext_temp = (short)TFA_GET_BF(handle, EXTTS); + return (twos(ext_temp)); +} + +/************************** tfa simple bitfield interfacing ***************************************/ +/* convenience functions */ +enum Tfa98xx_Error tfa98xx_set_volume_level(Tfa98xx_handle_t handle, unsigned short vol) +{ + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + if (vol > 255) /* restricted to 8 bits */ + vol = 255; + + /* 0x00 -> 0.0 dB + * 0x01 -> -0.5 dB + * ... + * 0xFE -> -127dB + * 0xFF -> muted + */ + + /* volume value is in the top 8 bits of the register */ + return -TFA_SET_BF(handle, VOL, (uint16_t)vol); +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa2(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + switch (mute) { + case Tfa98xx_Mute_Off: + TFA_SET_BF(handle, CFSMR, 0); + TFA_SET_BF(handle, CFSML, 0); + break; + case Tfa98xx_Mute_Amplifier: + case Tfa98xx_Mute_Digital: + TFA_SET_BF(handle, CFSMR, 1); + TFA_SET_BF(handle, CFSML, 1); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + return error; +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa1(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error; + unsigned short audioctrl_value; + unsigned short sysctrl_value; + int value; + + value = TFA_READ_REG(handle, CFSM); /* audio control register */ + if (value < 0) + return -value; + audioctrl_value = (unsigned short)value; + value = TFA_READ_REG(handle, AMPE); /* system control register */ + if (value < 0) + return -value; + sysctrl_value = (unsigned short)value; + + switch (mute) { + case Tfa98xx_Mute_Off: + /* previous state can be digital or amplifier mute, + * clear the cf_mute and set the enbl_amplifier bits + * + * To reduce PLOP at power on it is needed to switch the + * amplifier on with the DCDC in follower mode + * (enbl_boost = 0 ?). + * This workaround is also needed when toggling the + * powerdown bit! + */ + TFA_SET_BF_VALUE(handle, CFSM, 0, &audioctrl_value); + TFA_SET_BF_VALUE(handle, AMPE, 1, &sysctrl_value); + TFA_SET_BF_VALUE(handle, DCA, 1, &sysctrl_value); + break; + case Tfa98xx_Mute_Digital: + /* expect the amplifier to run */ + /* set the cf_mute bit */ + TFA_SET_BF_VALUE(handle, CFSM, 1, &audioctrl_value); + /* set the enbl_amplifier bit */ + TFA_SET_BF_VALUE(handle, AMPE, 1, &sysctrl_value); + /* clear active mode */ + TFA_SET_BF_VALUE(handle, DCA, 0, &sysctrl_value); + break; + case Tfa98xx_Mute_Amplifier: + /* clear the cf_mute bit */ + TFA_SET_BF_VALUE(handle, CFSM, 0, &audioctrl_value); + /* clear the enbl_amplifier bit and active mode */ + TFA_SET_BF_VALUE(handle, AMPE, 0, &sysctrl_value); + TFA_SET_BF_VALUE(handle, DCA, 0, &sysctrl_value); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + error = -TFA_WRITE_REG(handle, CFSM, audioctrl_value); + if (error) + return error; + error = -TFA_WRITE_REG(handle, AMPE, sysctrl_value); + return error; +} + +enum Tfa98xx_Error +tfa98xx_set_mute(Tfa98xx_handle_t handle, enum Tfa98xx_Mute mute) +{ + if (!tfa98xx_handle_is_open(handle)) { + pr_err("device not opened\n"); + return Tfa98xx_Error_NotOpen; + } + + if (tfa98xx_dev_family(handle) == 1) + return tfa98xx_set_mute_tfa1(handle, mute); + else + return tfa98xx_set_mute_tfa2(handle, mute); +} + +/****************** patching **********************************************************/ +static enum Tfa98xx_Error +tfa98xx_process_patch_file(Tfa98xx_handle_t handle, int length, + const unsigned char *bytes) +{ + unsigned short size; + int index = 0; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + while (index < length) { + size = bytes[index] + bytes[index + 1] * 256; + index += 2; + if ((index + size) > length) { + /* outside the buffer, error in the input data */ + return Tfa98xx_Error_Bad_Parameter; + } + + if (size > handles_local[handle].buffer_size) { + /* too big, must fit buffer */ + return Tfa98xx_Error_Bad_Parameter; + } + + error = tfa98xx_write_raw(handle, size, &bytes[index]); + if (error != Tfa98xx_Error_Ok) + break; + index += size; + } + return error; +} + + + +/* the patch contains a header with the following + * IC revision register: 1 byte, 0xFF means don't care + * XMEM address to check: 2 bytes, big endian, 0xFFFF means don't care + * XMEM value to expect: 3 bytes, big endian + */ +static enum Tfa98xx_Error +tfa98xx_check_ic_rom_version(Tfa98xx_handle_t handle, const unsigned char patchheader[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short checkrev, revid; + unsigned char lsb_revid; + unsigned short checkaddress; + int checkvalue; + int value = 0; + int status; + checkrev = patchheader[0]; + lsb_revid = handles_local[handle].rev & 0xff; /* only compare lower byte */ + + if ((checkrev != 0xFF) && (checkrev != lsb_revid)) + return Tfa98xx_Error_Not_Supported; + + checkaddress = (patchheader[1] << 8) + patchheader[2]; + checkvalue = + (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5]; + if (checkaddress != 0xFFFF) { + /* before reading XMEM, check if we can access the DSP */ + error = tfa98xx_dsp_system_stable(handle, &status); + if (error == Tfa98xx_Error_Ok) { + if (!status) { + /* DSP subsys not running */ + error = Tfa98xx_Error_DSP_not_running; + } + } + /* read register to check the correct ROM version */ + if (error == Tfa98xx_Error_Ok) { + error = + tfa98xx_dsp_read_mem(handle, checkaddress, 1, &value); + } + if (error == Tfa98xx_Error_Ok) { + if (value != checkvalue) { + pr_err("patch file romid type check failed [0x%04x]: expected 0x%02x, actual 0x%02x\n", + checkaddress, value, checkvalue); + error = Tfa98xx_Error_Not_Supported; + } + } + } else { /* == 0xffff */ + /* check if the revid subtype is in there */ + if ( checkvalue != 0xFFFFFF && checkvalue != 0) { + revid = patchheader[5]<<8 | patchheader[0]; /* full revid */ + if ( revid != handles_local[handle].rev) { + pr_err("patch file device type check failed: expected 0x%02x, actual 0x%02x\n", + handles_local[handle].rev, revid); + return Tfa98xx_Error_Not_Supported; + } + } + } + + return error; +} + + +#define PATCH_HEADER_LENGTH 6 +enum Tfa98xx_Error +tfa_dsp_patch(Tfa98xx_handle_t handle, int patchLength, + const unsigned char *patchBytes) +{ + enum Tfa98xx_Error error; + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + if (patchLength < PATCH_HEADER_LENGTH) + return Tfa98xx_Error_Bad_Parameter; + error = tfa98xx_check_ic_rom_version(handle, patchBytes); + if (Tfa98xx_Error_Ok != error) { + return error; + } + error = + tfa98xx_process_patch_file(handle, patchLength - PATCH_HEADER_LENGTH, + patchBytes + PATCH_HEADER_LENGTH); + return error; +} + +/****************** end patching **********************************************************/ + +TFA_INTERNAL enum Tfa98xx_Error +tfa98xx_wait_result(Tfa98xx_handle_t handle, int wait_retry_count) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int cf_status; /* the contents of the CF_STATUS register */ + int tries = 0; + do { + cf_status = TFA_GET_BF(handle, ACK); + if (cf_status < 0) + error = -cf_status; + tries++; + } + // i2c_cmd_ack + /* don't wait forever, DSP is pretty quick to respond (< 1ms) */ + while ((error == Tfa98xx_Error_Ok) && ((cf_status & CF_STATUS_I2C_CMD_ACK) == 0) + && (tries < wait_retry_count)); + if (tries >= wait_retry_count) { + /* something wrong with communication with DSP */ + error = Tfa98xx_Error_DSP_not_running; + } + return error; +} + +/* + * * support functions for data conversion + */ +/** + convert memory bytes to signed 24 bit integers + input: bytes contains "num_bytes" byte elements + output: data contains "num_bytes/3" int24 elements +*/ +void tfa98xx_convert_bytes2data(int num_bytes, const unsigned char bytes[], + int data[]) +{ + int i; /* index for data */ + int k; /* index for bytes */ + int d; + int num_data = num_bytes / 3; + _ASSERT((num_bytes % 3) == 0); + for (i = 0, k = 0; i < num_data; ++i, k += 3) { + d = (bytes[k] << 16) | (bytes[k + 1] << 8) | (bytes[k + 2]); + _ASSERT(d >= 0); + _ASSERT(d < (1 << 24)); /* max 24 bits in use */ + if (bytes[k] & 0x80) /* sign bit was set */ + d = -((1 << 24) - d); + + data[i] = d; + } +} + +/** + convert signed 24 bit integers to 32bit aligned bytes + input: data contains "num_bytes/3" int24 elements + output: bytes contains "num_bytes" byte elements +*/ +void tfa98xx_convert_data2bytes(int num_data, const int data[], + unsigned char bytes[]) +{ + int i; /* index for data */ + int k; /* index for bytes */ + int d; + /* note: cannot just take the lowest 3 bytes from the 32 bit + * integer, because also need to take care of clipping any + * value > 2&23 */ + for (i = 0, k = 0; i < num_data; ++i, k += 3) { + if (data[i] >= 0) + d = MIN(data[i], (1 << 23) - 1); + else { + /* 2's complement */ + d = (1 << 24) - MIN(-data[i], 1 << 23); + } + _ASSERT(d >= 0); + _ASSERT(d < (1 << 24)); /* max 24 bits in use */ + bytes[k] = (d >> 16) & 0xFF; /* MSB */ + bytes[k + 1] = (d >> 8) & 0xFF; + bytes[k + 2] = (d) & 0xFF; /* LSB */ + } +} + +/* + * DSP RPC message support functions + * depending on framework to be up and running + * need base i2c of memaccess (tfa1=0x70/tfa2=0x90) + */ + + +/* write dsp messages in function tfa_dsp_msg() */ +/* note the 'old' write_parameter() was more efficient because all i2c was in one burst transaction */ + +//TODO properly handle bitfields: state should be restored! (now it will change eg dmesg field to xmem) +enum Tfa98xx_Error tfa_dsp_msg_write(Tfa98xx_handle_t handle, int length, const char *buffer) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(handles_local[handle].buffer_size, 3); /* XMEM word size */ + int remaining_bytes = length; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t cfctl; + int value; + + value = TFA_READ_REG(handle, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(handle, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(handle, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(handle, MADD, 1); + + /* due to autoincrement in cf_ctrl, next write will happen at + * the next address */ + while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + /* else chunk_size remains at initialize value above */ + error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_MEM, + chunk_size, (const unsigned char *)buffer + offset); + remaining_bytes -= chunk_size; + offset += chunk_size; + } + + /* notify the DSP */ + if (error == Tfa98xx_Error_Ok) { + /* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + /* set the cf_req1 and cf_int bit */ + TFA_SET_BF_VALUE(handle, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(handle, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(handle, CFINT, cfctl); + } + + return error; +} + +enum Tfa98xx_Error tfa_dsp_msg_write_id(Tfa98xx_handle_t handle, int length, const char *buffer, uint8_t cmdid[3]) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(handles_local[handle].buffer_size, 3); /* XMEM word size */ + int remaining_bytes = length; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t cfctl; + int value; + + value = TFA_READ_REG(handle, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(handle, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(handle, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(handle, MADD, 1); + + /* write cmd-id */ + error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_MEM, 3, (const unsigned char *)cmdid); + + /* due to autoincrement in cf_ctrl, next write will happen at + * the next address */ + while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + /* else chunk_size remains at initialize value above */ + error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_MEM, + chunk_size, (const unsigned char *)buffer + offset); + remaining_bytes -= chunk_size; + offset += chunk_size; + } + + /* notify the DSP */ + if (error == Tfa98xx_Error_Ok) { + /* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + /* set the cf_req1 and cf_int bit */ + TFA_SET_BF_VALUE(handle, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(handle, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(handle, CFINT, cfctl); + } + + return error; +} + +/* +* status function used by tfa_dsp_msg() to retrieve command/msg status: +* return a <0 status of the DSP did not ACK. +*/ +enum Tfa98xx_Error tfa_dsp_msg_status(Tfa98xx_handle_t handle, int *pRpcStatus) { + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = tfa98xx_wait_result(handle, 2); /* 2 is only one try */ + if (error == Tfa98xx_Error_DSP_not_running) { + *pRpcStatus = -1; + return Tfa98xx_Error_Ok; + } + else if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_check_rpc_status(handle, pRpcStatus); + + return error; +} + +const char* tfa98xx_get_i2c_status_id_string(int status) +{ + const char* p_id_str; + + switch (status) + { + case Tfa98xx_DSP_Not_Running: + p_id_str = "No response from DSP"; + break; + case Tfa98xx_I2C_Req_Done: + p_id_str = "Ok"; + break; + case Tfa98xx_I2C_Req_Busy: + p_id_str = "Request is being processed"; + break; + case Tfa98xx_I2C_Req_Invalid_M_ID: + p_id_str = "Provided M-ID does not fit in valid rang [0..2]"; + break; + case Tfa98xx_I2C_Req_Invalid_P_ID: + p_id_str = "Provided P-ID is not valid in the given M-ID context"; + break; + case Tfa98xx_I2C_Req_Invalid_CC: + p_id_str = "Invalid channel configuration bits (SC|DS|DP|DC) combination"; + break; + case Tfa98xx_I2C_Req_Invalid_Seq: + p_id_str = "Invalid sequence of commands, in case the DSP expects some commands in a specific order"; + break; + case Tfa98xx_I2C_Req_Invalid_Param: + p_id_str = "Generic error"; + break; + case Tfa98xx_I2C_Req_Buffer_Overflow: + p_id_str = "I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed"; + break; + default: + p_id_str = "Unspecified error"; + } + + return p_id_str; +} + +enum Tfa98xx_Error tfa_dsp_msg_read(Tfa98xx_handle_t handle,int length, unsigned char *bytes){ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int burst_size; /* number of words per burst size */ + int bytes_per_word = 3; + int num_bytes; + int offset = 0; + unsigned short start_offset=2; /* msg starts @xmem[2] ,[1]=cmd */ + + if ( length > TFA2_MAX_PARAM_SIZE) + return Tfa98xx_Error_Bad_Parameter; + + TFA_SET_BF(handle, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM); + error = -TFA_WRITE_REG(handle, MADD, start_offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes = length; /* input param */ + while (num_bytes > 0) { + burst_size = ROUND_DOWN(handles_local[handle].buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + error = tfa98xx_read_data(handle, FAM_TFA98XX_CF_MEM, burst_size, bytes + offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes -= burst_size; + offset += burst_size; + } + + return Tfa98xx_Error_Ok; +} + +/* + * write/read raw msg functions : + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + */ +#define MAX_WORDS (300) +enum Tfa98xx_Error tfa_dsp_msg(Tfa98xx_handle_t handle, int length, const char *buf) +{ + enum Tfa98xx_Error error; + int tries, rpc_status = Tfa98xx_I2C_Req_Done; + + /* write the message and notify the DSP */ + error = tfa_dsp_msg_write(handle, length, buf); + if( error != Tfa98xx_Error_Ok) + return error; + + /* get the result from the DSP (polling) */ + for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) { + error = tfa_dsp_msg_status(handle, &rpc_status); + if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) + break; + /* If the rpc status is a specific error we want to know it. + * If it is busy or not running it should retry + */ + if(rpc_status != Tfa98xx_I2C_Req_Busy && rpc_status != Tfa98xx_DSP_Not_Running) + break; + } + + //if (tfa98xx_runtime_verbose) + // PRINT("Number of tries: %d \n", TFA98XX_WAITRESULT_NTRIES-tries); + + if (rpc_status != Tfa98xx_I2C_Req_Done) { + /* DSP RPC call returned an error */ + error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); + pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); + } + + return error; +} + +/** + * write/read raw msg functions: + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * An ID is added to modify the command-ID + */ +enum Tfa98xx_Error tfa_dsp_msg_id(Tfa98xx_handle_t handle, int length, const char *buf, uint8_t cmdid[3]) +{ + enum Tfa98xx_Error error; + int tries, rpc_status = Tfa98xx_I2C_Req_Done; + + /* write the message and notify the DSP */ + error = tfa_dsp_msg_write_id(handle, length, buf, cmdid); + if( error != Tfa98xx_Error_Ok) + return error; + + /* get the result from the DSP (polling) */ + for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) { + error = tfa_dsp_msg_status(handle, &rpc_status); + if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) + break; + } + + //if (tfa98xx_runtime_verbose) + //PRINT("Number of tries: %d \n", TFA98XX_WAITRESULT_NTRIES-tries); + + if (rpc_status != Tfa98xx_I2C_Req_Done) { + /* DSP RPC call returned an error */ + error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); + pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); + } + + return error; +} + +/* read the return code for the RPC call */ +TFA_INTERNAL enum Tfa98xx_Error +tfa98xx_check_rpc_status(Tfa98xx_handle_t handle, int *pRpcStatus) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + /* the value to sent to the * CF_CONTROLS register: cf_req=00000000, + * cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + unsigned short cf_ctrl = 0x0002; + /* memory address to be accessed (0: Status, 1: ID, 2: parameters) */ + unsigned short cf_mad = 0x0000; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + if (pRpcStatus == 0) + return Tfa98xx_Error_Bad_Parameter; + + /* 1) write DMEM=XMEM to the DSP XMEM */ + { + /* minimize the number of I2C transactions by making use of the autoincrement in I2C */ + unsigned char buffer[4]; + /* first the data for CF_CONTROLS */ + buffer[0] = (unsigned char)((cf_ctrl >> 8) & 0xFF); + buffer[1] = (unsigned char)(cf_ctrl & 0xFF); + /* write the contents of CF_MAD which is the subaddress following CF_CONTROLS */ + buffer[2] = (unsigned char)((cf_mad >> 8) & 0xFF); + buffer[3] = (unsigned char)(cf_mad & 0xFF); + error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer); + } + if (error == Tfa98xx_Error_Ok) { + /* read 1 word (24 bit) from XMEM */ + error = tfa98xx_dsp_read_mem(handle, 0, 1, pRpcStatus); + } + + return error; +} + +/***************************** xmem only **********************************/ +enum Tfa98xx_Error +tfa98xx_dsp_read_mem(Tfa98xx_handle_t handle, + unsigned int start_offset, int num_words, int *pValues) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char *bytes; + int burst_size; /* number of words per burst size */ + const int bytes_per_word = 3; + int dmem; + int num_bytes; + int *p; + + bytes = (unsigned char *)kmalloc(num_words*bytes_per_word, GFP_KERNEL); + if (bytes == NULL) + return Tfa98xx_Error_Fail; + + /* If no offset is given, assume XMEM! */ + if(((start_offset>>16) & 0xf) > 0 ) + dmem = (start_offset>>16) & 0xf; + else + dmem = Tfa98xx_DMEM_XMEM; + + /* Remove offset from adress */ + start_offset = start_offset & 0xffff; + num_bytes = num_words * bytes_per_word; + p = pValues; + + TFA_SET_BF(handle, DMEM, (uint16_t)dmem); + error = -TFA_WRITE_REG(handle, MADD, (unsigned short)start_offset); + if (error != Tfa98xx_Error_Ok) + goto tfa98xx_dsp_read_mem_exit; + + for (; num_bytes > 0;) { + burst_size = ROUND_DOWN(handles_local[handle].buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + + _ASSERT(burst_size <= sizeof(bytes)); + error = tfa98xx_read_data( handle, FAM_TFA98XX_CF_MEM, burst_size, bytes); + if (error != Tfa98xx_Error_Ok) + goto tfa98xx_dsp_read_mem_exit; + + tfa98xx_convert_bytes2data(burst_size, bytes, p); + + num_bytes -= burst_size; + p += burst_size / bytes_per_word; + } + +tfa98xx_dsp_read_mem_exit: + kfree(bytes); + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_mem_word(Tfa98xx_handle_t handle, unsigned short address, int value, int memtype) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3]; + + TFA_SET_BF(handle, DMEM, (uint16_t)memtype); + + error = -TFA_WRITE_REG(handle, MADD, address); + if (error != Tfa98xx_Error_Ok) + return error; + + tfa98xx_convert_data2bytes(1, &value, bytes); + error = tfa98xx_write_data(handle, FAM_TFA98XX_CF_MEM, 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa_cont_write_filterbank(int device, nxpTfaFilter_t *filter) +{ + unsigned char biquad_index; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + for(biquad_index=0;biquad_index<10;biquad_index++) { + if (filter[biquad_index].enabled ) { + error = tfa_dsp_cmd_id_write(device, MODULE_BIQUADFILTERBANK, + biquad_index+1, //start @1 + sizeof(filter[biquad_index].biquad.bytes), + filter[biquad_index].biquad.bytes); + } else { + error = Tfa98xx_DspBiquad_Disable(device, biquad_index+1); + } + if (error) return error; + + } + return error; +} + +enum Tfa98xx_Error +Tfa98xx_DspBiquad_Disable(Tfa98xx_handle_t handle, int biquad_index) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int coeff_buffer[BIQUAD_COEFF_SIZE]; + unsigned char bytes[3 + BIQUAD_COEFF_SIZE * 3]; + + if (biquad_index > TFA98XX_BIQUAD_NUM) + return Tfa98xx_Error_Bad_Parameter; + if (biquad_index < 1) + return Tfa98xx_Error_Bad_Parameter; + + /* make opcode */ + + /* set in correct order and format for the DSP */ + coeff_buffer[0] = (int) - 8388608; /* -1.0f */ + coeff_buffer[1] = 0; + coeff_buffer[2] = 0; + coeff_buffer[3] = 0; + coeff_buffer[4] = 0; + coeff_buffer[5] = 0; + /* convert to packed 24 bits data */ + tfa98xx_convert_data2bytes(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[3]); + + bytes[0] = 0; + bytes[1] = MODULE_BIQUADFILTERBANK+128; + bytes[2] = (unsigned char)biquad_index; + + error = tfa_dsp_msg(handle, 3 + BIQUAD_COEFF_SIZE * 3, (char *)bytes); + + return error; +} + +/* wrapper for dsp_msg that adds opcode */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + const unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char *buffer; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + buffer = kmalloc(3 + num_bytes, GFP_KERNEL); + if (buffer == NULL) + return Tfa98xx_Error_Fail; + + buffer[0] = handles_local[handle].spkr_select; + buffer[1] = module_id + 128; + buffer[2] = param_id; + + memcpy(&buffer[3], data, num_bytes); + + error = tfa_dsp_msg(handle, 3 + num_bytes, (char *)buffer); + + kfree(buffer); + + return error; +} + +/* wrapper for dsp_msg that adds opcode */ +/* this is as the former tfa98xx_dsp_get_param() */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write_read(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[3]; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + buffer[0] = handles_local[handle].spkr_select; + buffer[1] = module_id + 128; + buffer[2] = param_id; + + error = tfa_dsp_msg(handle, sizeof(unsigned char[3]), (char *)buffer); + if ( error!= Tfa98xx_Error_Ok) + return error; + /* read the data from the dsp */ + error = tfa_dsp_msg_read(handle, num_bytes, data); + + return error; +} + +/* wrapper for dsp_msg that adds opcode and 3 bytes required for coefs */ +enum Tfa98xx_Error tfa_dsp_cmd_id_coefs(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[6]; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + buffer[0] = handles_local[handle].spkr_select; + buffer[1] = module_id + 128; + buffer[2] = param_id; + buffer[3] = 0; + buffer[4] = 0; + buffer[5] = 0; + + error = tfa_dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = tfa_dsp_msg_read(handle, num_bytes, data); + + return error; +} + +/* wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics */ +enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[6]; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + buffer[0] = handles_local[handle].spkr_select; + buffer[1] = module_id + 128; + buffer[2] = param_id; + buffer[3] = 0; + buffer[4] = 0; + buffer[5] = (unsigned char)index_subband; + + error = tfa_dsp_msg(handle, sizeof(unsigned char[6]), (char *)buffer); + if(error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = tfa_dsp_msg_read(handle, num_bytes, data); + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_preset(Tfa98xx_handle_t handle, int length, + const unsigned char *p_preset_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_preset_bytes != 0) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + error = tfa_dsp_cmd_id_write(handle, MODULE_SPEAKERBOOST, + SB_PARAM_SET_PRESET, length, + p_preset_bytes); + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + return error; +} + +/* + * get features from MTP + */ +enum Tfa98xx_Error +tfa98xx_dsp_get_hw_feature_bits(Tfa98xx_handle_t handle, int *features) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint32_t value; + uint16_t mtpbf; + + /* return the cache data if it's valid */ + if (handles_local[handle].hw_feature_bits != -1) { + *features = handles_local[handle].hw_feature_bits; + } else { + /* for tfa1 check if we have clock */ + if (tfa98xx_dev_family(handle) == 1) { + int status; + tfa98xx_dsp_system_stable(handle, &status); + if (!status) { + get_hw_features_from_cnt(handle, features); + /* skip reading MTP: */ + return (*features == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; + } + mtpbf=0x850f; /* MTP5 for tfa1,16 bits */ + } else + mtpbf=0xf907; /* MTP9 for tfa2, 8 bits */ + value = tfa_read_reg(handle, mtpbf) & 0xffff; + *features = handles_local[handle].hw_feature_bits = value; + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(Tfa98xx_handle_t handle, int features[2]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3 * 2]; + + /* return the cache data if it's valid */ + if (handles_local[handle].sw_feature_bits[0] != -1) { + features[0] = handles_local[handle].sw_feature_bits[0]; + features[1] = handles_local[handle].sw_feature_bits[1]; + } else { + /* for tfa1 check if we have clock */ + if (tfa98xx_dev_family(handle) == 1) { + int status; + tfa98xx_dsp_system_stable(handle, &status); + if (!status) { + get_sw_features_from_cnt(handle, features); + /* skip reading MTP: */ + return (features[0] == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; + } + } + error = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, + FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes); + + if (error != Tfa98xx_Error_Ok) { + /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ + return error; + } + tfa98xx_convert_bytes2data(sizeof(bytes), bytes, features); + } + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_get_state_info(Tfa98xx_handle_t handle, unsigned char bytes[], unsigned int *statesize) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int bSupportFramework = 0; + unsigned int stateSize = 9; + + err = tfa98xx_dsp_support_framework(handle, &bSupportFramework); + if (err == Tfa98xx_Error_Ok) { + if (bSupportFramework) { + err = tfa_dsp_cmd_id_write_read(handle, MODULE_FRAMEWORK, + FW_PARAM_GET_STATE, 3 * stateSize, bytes); + } else { + /* old ROM code, ask SpeakerBoost and only do first portion */ + stateSize = 8; + err = tfa_dsp_cmd_id_write_read(handle, MODULE_SPEAKERBOOST, + SB_PARAM_GET_STATE, 3 * stateSize, bytes); + } + } + + *statesize = stateSize; + + return err; +} + +enum Tfa98xx_Error tfa98xx_dsp_support_drc(Tfa98xx_handle_t handle, int *pbSupportDrc) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + *pbSupportDrc = 0; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + if (handles_local[handle].supportDrc != supportNotSet) { + *pbSupportDrc = (handles_local[handle].supportDrc == supportYes); + } else { + int featureBits[2]; + + error = tfa98xx_dsp_get_sw_feature_bits(handle, featureBits); + if (error == Tfa98xx_Error_Ok) { + /* easy case: new API available */ + /* bit=0 means DRC enabled */ + *pbSupportDrc = (featureBits[0] & FEATURE1_DRC) == 0; + } else if (error == Tfa98xx_Error_RpcParamId) { + /* older ROM code, doesn't support it */ + *pbSupportDrc = 0; + error = Tfa98xx_Error_Ok; + } + /* else some other error, return transparently */ + /* pbSupportDrc only changed when error == Tfa98xx_Error_Ok */ + + if (error == Tfa98xx_Error_Ok) { + handles_local[handle].supportDrc = *pbSupportDrc ? supportYes : supportNo; + } + } + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(Tfa98xx_handle_t handle, int *pbSupportFramework) +{ + int featureBits[2] = { 0, 0 }; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + _ASSERT(pbSupportFramework != 0); + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + if (handles_local[handle].supportFramework != supportNotSet) { + if(handles_local[handle].supportFramework == supportNo) + *pbSupportFramework = 0; + else + *pbSupportFramework = 1; + } else { + error = tfa98xx_dsp_get_sw_feature_bits(handle, featureBits); + if (error == Tfa98xx_Error_Ok) { + *pbSupportFramework = 1; + handles_local[handle].supportFramework = supportYes; + } else { + *pbSupportFramework = 0; + handles_local[handle].supportFramework = supportNo; + error = Tfa98xx_Error_Ok; + } + } + + /* *pbSupportFramework only changed when error == Tfa98xx_Error_Ok */ + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_speaker_parameters(Tfa98xx_handle_t handle, + int length, + const unsigned char *p_speaker_bytes) +{ + enum Tfa98xx_Error error; + if (p_speaker_bytes != 0) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + /* Use long WaitResult retry count */ + error = tfa_dsp_cmd_id_write( + handle, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_LSMODEL, length, + p_speaker_bytes); + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) + { + int bSupportDrc; + + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(handle, &bSupportDrc); + if (error != Tfa98xx_Error_Ok) + return error; + + if (bSupportDrc) { + /* Need to set AgcGainInsert back to PRE, + * as the SetConfig forces it to POST */ + uint8_t bytes[3] = {0, 0, 0}; + + error = tfa_dsp_cmd_id_write(handle, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + sizeof(bytes), + bytes); + } + } +#endif + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_config(Tfa98xx_handle_t handle, int length, + const unsigned char *p_config_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = tfa_dsp_cmd_id_write(handle, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_CONFIG, length, + p_config_bytes); + +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) + { + int bSupportDrc; + + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(handle, &bSupportDrc); + if (error != Tfa98xx_Error_Ok) + return error; + + if (bSupportDrc) { + /* Need to set AgcGainInsert back to PRE, + * as the SetConfig forces it to POST */ + uint8_t bytes[3] = {0, 0, 0}; + + error = tfa_dsp_cmd_id_write(handle, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + sizeof(bytes), + bytes); + } + } +#endif + + return error; +} + +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) +/* load all the parameters for the DRC settings from a file */ +enum Tfa98xx_Error tfa98xx_dsp_write_drc(Tfa98xx_handle_t handle, + int length, const unsigned char *p_drc_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_drc_bytes != 0) { + error = tfa_dsp_cmd_id_write(handle, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_DRC, length, + p_drc_bytes); + + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + return error; +} +#endif + +enum Tfa98xx_Error tfa98xx_powerdown(Tfa98xx_handle_t handle, int powerdown) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + TFA_SET_BF(handle, PWDN, (uint16_t)powerdown); + + return error; +} + +enum Tfa98xx_Error +tfa98xx_select_mode(Tfa98xx_handle_t handle, enum Tfa98xx_Mode mode) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + if (error == Tfa98xx_Error_Ok) { + switch (mode) { + + default: + error = Tfa98xx_Error_Bad_Parameter; + } + } + + return error; +} + +int tfa_set_bf(Tfa98xx_handle_t dev_idx, const uint16_t bf, const uint16_t value) +{ + enum Tfa98xx_Error err; + uint16_t regvalue, msk, oldvalue; + + /* + * bitfield enum: + * - 0..3 : len + * - 4..7 : pos + * - 8..15 : address + */ + uint8_t len = bf & 0x0f; + uint8_t pos = (bf >> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = tfa98xx_read_register16(dev_idx, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + oldvalue = regvalue; + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = tfa98xx_read_register16(dev_idx, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = tfa98xx_read_register16(dev_idx, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + msk = ((1<<(len+1))-1)<>pos; + + return value; +} + +int tfa_set_bf_value(const uint16_t bf, const uint16_t bf_value, uint16_t *p_reg_value) +{ + uint16_t regvalue, msk; + + /* + * bitfield enum: + * - 0..3 : len + * - 4..7 : pos + * - 8..15 : address + */ + uint8_t len = bf & 0x0f; + uint8_t pos = (bf >> 4) & 0x0f; + + regvalue = *p_reg_value; + + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + + msk = ((1<<(len+1))-1)<> pos; + + return value; +} + + +int tfa_write_reg(Tfa98xx_handle_t dev_idx, const uint16_t bf, const uint16_t reg_value) +{ + enum Tfa98xx_Error err; + + /* bitfield enum - 8..15 : address */ + uint8_t address = (bf >> 8) & 0xff; + + err = tfa98xx_write_register16(dev_idx, address, reg_value); + if (err) + return -err; + + return 0; +} + +int tfa_read_reg(Tfa98xx_handle_t dev_idx, const uint16_t bf) +{ + enum Tfa98xx_Error err; + uint16_t regvalue; + + /* bitfield enum - 8..15 : address */ + uint8_t address = (bf >> 8) & 0xff; + + err = tfa98xx_read_register16(dev_idx, address, ®value); + if (err) + return -err; + + return regvalue; +} + +/* + * powerup the coolflux subsystem and wait for it + */ +enum Tfa98xx_Error tfa_cf_powerup(Tfa98xx_handle_t handle) { + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int tries, status; + + /* power on the sub system */ + TFA_SET_BF_VOLATILE(handle, PWDN, 0); + + // wait until everything is stable, in case clock has been off + if (tfa98xx_runtime_verbose) + pr_info("Waiting for DSP system stable...\n"); + for ( tries=CFSTABLE_TRIES; tries > 0; tries-- ) { + err = tfa98xx_dsp_system_stable(handle, &status); + _ASSERT(err == Tfa98xx_Error_Ok); + if ( status ) + break; + else + msleep_interruptible(10); /* wait 10ms to avoid busload */ + } + if (tries==0) {// timedout + pr_err("DSP subsystem start timed out\n"); + return Tfa98xx_Error_StateTimedOut; + } + + return err; +} + +/* + * Enable/Disable the I2S output for TFA1 devices + * without TDM interface + */ +static enum Tfa98xx_Error tfa98xx_aec_output(Tfa98xx_handle_t handle, int enable) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ((tfa98xx_get_device_dai(handle) & Tfa98xx_DAI_TDM) == Tfa98xx_DAI_TDM) + return err; + + if (tfa98xx_dev_family(handle) == 1) + err = -tfa_set_bf(handle, TFA1_BF_I2SDOE, (enable!=0)); + else { + pr_err("I2SDOE on unsupported family\n"); + err = Tfa98xx_Error_Not_Supported; + } + + return err; +} + +/* + * Print the current state of the hardware manager + * Device manager status information, man_state from TFA9888_N1B_I2C_regmap_V12 + */ +enum Tfa98xx_Error show_current_state(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int manstate = -1; + + if (tfa98xx_dev_family(handle) == 2) { + manstate = TFA_GET_BF(handle, MANSTATE); + if (manstate < 0) + return -manstate; + } + + pr_debug("Current HW manager state: "); + + switch(manstate) { + case 0: pr_debug("power_down_state \n"); + break; + case 1: pr_debug("wait_for_source_settings_state \n"); + break; + case 2: pr_debug("connnect_pll_input_state \n"); + break; + case 3: pr_debug("disconnect_pll_input_state \n"); + break; + case 4: pr_debug("enable_pll_state \n"); + break; + case 5: pr_debug("enable_cgu_state \n"); + break; + case 6: pr_debug("init_cf_state \n"); + break; + case 7: pr_debug("enable_amplifier_state \n"); + break; + case 8: pr_debug("alarm_state \n"); + break; + case 9: pr_debug("operating_state \n"); + break; + case 10: pr_debug("mute_audio_state \n"); + break; + case 11: pr_debug("disable_cgu_pll_state \n"); + break; + default: + pr_debug("Unable to find current state \n"); + break; + } + + return err; +} + +/* + * start the speakerboost algorithm + * this implies a full system startup when the system was not already started + * + */ +enum Tfa98xx_Error tfaRunSpeakerBoost(Tfa98xx_handle_t handle, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int value; + int istap_prof = 0; + + if ( force ) { + err= tfaRunColdStartup(handle, profile); + if ( err ) return err; + // DSP is running now + } + + value = TFA_GET_BF(handle, ACS); + +#ifdef __KERNEL__ /* TODO try to combine this with the pr_debug below */ + tfa98xx_trace_printk("%s %sstart\n", + tfaContDeviceName(handle), + value ? "cold" : "warm"); +#endif + /* Check if next profile is a tap profile */ + istap_prof = tfaContIsTapProfile(handle, profile); + + if ((value == 1) && (!istap_prof)) { + /* Run startup and write all files */ + err = tfaRunSpeakerStartup(handle, force, profile); + if ( err ) return err; + + /* Save the current profile and set the vstep to 0 */ + /* This needs to be overwriten even in CF bypass */ + tfa_set_swprof(handle, (unsigned short)profile); + tfa_set_swvstep(handle, 0); + + /* Startup with CF in bypass then return here */ + if (TFA_GET_BF(handle, CFE) == 0) { + return err; + } + +#ifdef __KERNEL__ /* TODO check if this can move to the tfa98xx.c */ + /* Necessary here for early calibrate (MTPEX + ACS) */ + tfa98xx_apply_deferred_calibration(handle); +#endif + /* calibrate */ + err = tfaRunSpeakerCalibration(handle, profile); + } else if (istap_prof) { + /* Save the current profile and set the vstep to 0 */ + /* This needs to be overwriten even in CF bypass and tap*/ + tfa_set_swprof(handle, (unsigned short)profile); + tfa_set_swvstep(handle, 0); + } + + return err; +} + +enum Tfa98xx_Error tfaRunSpeakerStartup(Tfa98xx_handle_t handle, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + pr_debug("coldstart%s :", force? " (forced)":""); + + if ( !force ) { // in case of force CF already runnning + err = tfaRunStartup(handle, profile); + PRINT_ASSERT(err); + if ( err ) + return err; + /* Startup with CF in bypass then return here */ + if (TFA_GET_BF(handle, CFE) == 0) + return err; + + err = tfaRunStartDSP(handle); + if ( err ) + return err; + } + // DSP is running now + tfa98xxSetCalibrateOnce(handle); + // NOTE that ACS may be active + // no DSP reset/sample rate may be done until configured (SBSL) + // For the first configuration the DSP expects at least + // the speaker, config and a preset. + // Therefore all files from the device list as well as the file + // from the default profile are loaded before SBSL is set. + // + // Note that the register settings were already done before loading the patch + // + // write all the files from the device list (speaker, vstep and all messages) + err = tfaContWriteFiles(handle); + if (err) + return err; + + // write all the files from the profile list (typically preset) + err = tfaContWriteFilesProf(handle, profile, 0); // use volumestep 0 + if (err) + return err; + + return err; +} + +/* + * Run calibration + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(Tfa98xx_handle_t handle, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int calibrateDone, spkr_count = 0; + /* Avoid warning in user-space */ + profile=profile; + +#ifdef __KERNEL__ /* Necessary otherwise we are thrown out of operating mode in kernel (because of internal clock) */ + if((strstr(tfaContProfileName(handle, profile), ".cal") == NULL) && (tfa98xx_dev_family(handle) == 2)) + { + TFA_SET_BF_VOLATILE(handle, SBSL, 1); + } + else if (tfa98xx_dev_family(handle) != 2) +#endif + TFA_SET_BF_VOLATILE(handle, SBSL, 1); + + /* return if there is no audio running */ + if (TFA_GET_BF(handle, NOCLK) && tfa98xx_dev_family(handle) == 2) + return Tfa98xx_Error_NoClock; + + /* When MTPOTC is set (cal=once) unlock key2 */ + if (TFA_GET_BF(handle, MTPOTC) == 1) { + tfa98xx_key2(handle, 0); + } + + /* await calibration, this should return ok */ + err = tfaRunWaitCalibration(handle, &calibrateDone); + if (err == Tfa98xx_Error_Ok) { + err = tfa_dsp_get_calibration_impedance(handle); + PRINT_ASSERT(err); + } + + /* Give reason why calibration failed! */ + if (err != Tfa98xx_Error_Ok) { + if ((tfa98xx_dev_family(handle) == 2 && TFA_GET_BF(handle, REFCKSEL) == 1)) { + pr_err("Unable to calibrate the device with the internal clock! \n"); + } + } + + if (err == Tfa98xx_Error_Ok) { + err = tfa98xx_supported_speakers(handle, &spkr_count); + + if (spkr_count == 1) { + pr_debug(" %d mOhms \n", handles_local[handle].mohm[0]); + } else { + pr_debug(" Prim:%d mOhms, Sec:%d mOhms\n", + handles_local[handle].mohm[0], + handles_local[handle].mohm[1]); + } + } + + /* When MTPOTC is set (cal=once) re-lock key2 */ + if (TFA_GET_BF(handle, MTPOTC) == 1) { + tfa98xx_key2(handle, 1); + } + + return err; +} + +/* + * Set the debug option + */ +void tfa_verbose(int level) +{ + tfa98xx_trace_level = level; + tfa98xx_runtime_verbose = level!=0; /* any non-zero */ + tfa_cnt_verbose(level); +} + +enum Tfa98xx_Error tfaRunColdboot(Tfa98xx_handle_t handle, int state) +{ +#define CF_CONTROL 0x8100 + enum Tfa98xx_Error err=Tfa98xx_Error_Ok; + int tries = 10; + + /* repeat set ACS bit until set as requested */ + while ( state != TFA_GET_BF(handle, ACS)) { + /* set colstarted in CF_CONTROL to force ACS */ + err = tfa98xx_dsp_write_mem_word(handle, CF_CONTROL, state, Tfa98xx_DMEM_IOMEM); + PRINT_ASSERT(err); + + if (tries-- == 0) { + pr_debug("coldboot (ACS) did not %s\n", state ? "set":"clear"); + return Tfa98xx_Error_Other; + } + } + + return err; +} + + + +/* + * load the patch if any + * else tell no loaded + */ +static enum Tfa98xx_Error tfa_run_load_patch(Tfa98xx_handle_t handle) +{ + return tfaContWritePatch(handle); +} + +/* + * this will load the patch witch will implicitly start the DSP + * if no patch is available the DPS is started immediately + */ +enum Tfa98xx_Error tfaRunStartDSP(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfa_run_load_patch(handle); + if (err) { /* patch load is fatal so return immediately*/ + return err; + } + + /* Clear count_boot, should be reset to 0 before the DSP reset is released */ + err = tfa98xx_dsp_write_mem_word(handle, 512, 0, Tfa98xx_DMEM_XMEM); + PRINT_ASSERT(err); + + /* Reset DSP once for sure after initializing */ + if ( err == Tfa98xx_Error_Ok) { + err = tfa98xx_dsp_reset(handle, 0); /* in pair of tfa98xx_init() - tfaRunStartup() */ + PRINT_ASSERT(err); + } + + /* Sample rate is needed to set the correct tables */ + err = tfa98xx_dsp_write_tables(handle, TFA_GET_BF(handle, AUDFS)); + PRINT_ASSERT(err); + + return err; +} + +/* + * start the clocks and wait until the AMP is switching + * on return the DSP sub system will be ready for loading + */ +enum Tfa98xx_Error tfaRunStartup(Tfa98xx_handle_t handle, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(handle); + int tries, status, i, noinit=0; + + if(dev == NULL) + return Tfa98xx_Error_Fail; + + /* process the device list to see if the user implemented the noinit */ + for(i=0;ilength;i++) { + if (dev->list[i].type == dscNoInit) { + noinit=1; + break; + } + } + + if(!noinit) { + /* load the optimal TFA98XX in HW settings */ + err = tfa98xx_init(handle); + PRINT_ASSERT(err); + } else { + pr_debug("\nWarning: No init keyword found in the cnt file. Init is skipped! \n"); + } + + /* I2S settings to define the audio input properties + * these must be set before the subsys is up */ + // this will run the list until a non-register item is encountered + err = tfaContWriteRegsDev(handle); // write device register settings + PRINT_ASSERT(err); + // also write register the settings from the default profile + // NOTE we may still have ACS=1 so we can switch sample rate here + err = tfaContWriteRegsProf(handle, profile); + PRINT_ASSERT(err); + + if(tfa98xx_dev_family(handle) == 2) { + /* Factory trimming for the Boost converter */ + tfa_factory_trimmer(handle); + } + + /* leave power off state */ + err = tfa98xx_powerdown(handle, 0); + PRINT_ASSERT(err); + + if (tfa98xx_dev_family(handle) == 2) { + /* signal that the clock settings are done + * - PLL can start */ + TFA_SET_BF_VOLATILE(handle, MANSCONF, 1); + } + switch (TFA_GET_BF(handle, REV) & 0xff) + { + case 0x80: + err = tfa98xx_dsp_reset(handle, 1); + pr_debug("tfaRunStartup Reset DSP after power on\n"); + break; + default: + break; + } + + /* wait until the PLL is ready + * note that the DSP CPU is not running (RST=1) */ + if (tfa98xx_runtime_verbose) { + if (TFA_GET_BF(handle, NOCLK) && (tfa98xx_dev_family(handle) == 2)) + pr_debug("Using internal clock\n"); + pr_debug("Waiting for DSP system stable...\n"); + } + for ( tries=1; tries < CFSTABLE_TRIES; tries++ ) { + err = tfa98xx_dsp_system_stable(handle, &status); + _ASSERT(err == Tfa98xx_Error_Ok); + if ( status ) + break; + else + msleep_interruptible(10); /* wait 10ms to avoid busload */ + } + if (tries == CFSTABLE_TRIES) { + if (tfa98xx_runtime_verbose) pr_debug("Timed out\n"); + return Tfa98xx_Error_StateTimedOut; + } else + if (tfa98xx_runtime_verbose) pr_debug(" OK (tries=%d)\n", tries); + + if (tfa98xx_runtime_verbose && tfa98xx_dev_family(handle) == 2) + err = show_current_state(handle); + + return err; +} + +/* + * run the startup/init sequence and set ACS bit + */ +enum Tfa98xx_Error tfaRunColdStartup(Tfa98xx_handle_t handle, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfaRunStartup(handle, profile); + PRINT_ASSERT(err); + if (err) + return err; + + /* force cold boot */ + err = tfaRunColdboot(handle, 1); // set ACS + PRINT_ASSERT(err); + if (err) + return err; + + /* start */ + err = tfaRunStartDSP(handle); + PRINT_ASSERT(err); + + return err; +} + +/* + * + */ +enum Tfa98xx_Error tfaRunMute(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int status; + int tries = 0; + + /* signal the TFA98XX to mute */ + if(tfa98xx_dev_family(handle) == 1) { + err = tfa98xx_set_mute(handle, Tfa98xx_Mute_Amplifier); + + if(err == Tfa98xx_Error_Ok) { + /* now wait for the amplifier to turn off */ + do { + status = TFA_GET_BF(handle, SWS); + if (status != 0) + msleep_interruptible(10); /* wait 10ms to avoid busload */ + else + break; + tries++; + } while (tries < AMPOFFWAIT_TRIES); + + + if ( tfa98xx_runtime_verbose ) + pr_debug("-------------------- muted --------------------\n"); + + /*The amplifier is always switching*/ + if (tries == AMPOFFWAIT_TRIES) + return Tfa98xx_Error_Other; + } + } + + return err; +} +/* + * + */ +enum Tfa98xx_Error tfaRunUnmute(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + /* signal the TFA98XX to mute */ + err = tfa98xx_set_mute(handle, Tfa98xx_Mute_Off); + + if ( tfa98xx_runtime_verbose ) + pr_debug("-------------------unmuted ------------------\n"); + + return err; +} + + +/* + * wait for calibrateDone + */ +enum Tfa98xx_Error tfaRunWaitCalibration(Tfa98xx_handle_t handle, int *calibrateDone) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int tries = 0, mtp_busy = 1, tries_mtp_busy = 0; + + *calibrateDone = 0; + + /* in case of calibrate once wait for MTPEX */ + if (TFA_GET_BF(handle, MTPOTC)) { + // Check if MTP_busy is clear! + while (tries_mtp_busy < MTPBWAIT_TRIES) + { + mtp_busy = TFA_GET_BF(handle, MTPB); + if (mtp_busy == 1) + msleep_interruptible(10); /* wait 10ms to avoid busload */ + else + break; + tries_mtp_busy++; + } + + if (tries_mtp_busy < MTPBWAIT_TRIES) { + /* Because of the msleep TFA98XX_API_WAITRESULT_NTRIES is way to long! + * Setting this to 25 will take it atleast 25*50ms = 1.25 sec + */ + while ( (*calibrateDone == 0) && (tries < MTPEX_WAIT_NTRIES)) { + *calibrateDone = TFA_GET_BF(handle, MTPEX); + if(*calibrateDone == 1) + break; + msleep_interruptible(50); /* wait 50ms to avoid busload */ + tries++; + } + + if (tries >= MTPEX_WAIT_NTRIES) { + tries = TFA98XX_API_WAITRESULT_NTRIES; + } + } else { + pr_err("MTP bussy after %d tries\n", MTPBWAIT_TRIES); + } + } + + /* poll xmem for calibrate always + * calibrateDone = 0 means "calibrating", + * calibrateDone = -1 (or 0xFFFFFF) means "fails" + * calibrateDone = 1 means calibration done + */ + while ((*calibrateDone != 1) && (tries= 0) + next_profile = cal_profile; + } + /* Check if next profile is a tap profile */ + istap_prof = tfaContIsTapProfile(dev, next_profile); + + /* tfaRun_SpeakerBoost implies un-mute */ + if (tfa98xx_runtime_verbose) { + pr_debug("active_profile:%s, next_profile:%s\n", + tfaContProfileName(dev,active_profile ), + tfaContProfileName(dev, next_profile)); + pr_debug("Starting device [%s]\n", tfaContDeviceName(dev)); + + if(tfa98xx_dev_family(dev) == 2) { + err = show_current_state(dev); + } + } + + /* enable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(dev, 1); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + + /* Check if we need coldstart or ACS is set */ + err = tfaRunSpeakerBoost(dev, 0, next_profile); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + + active_profile = tfa_get_swprof(dev); + + /* After loading calibration profile we need to load acoustic shock profile */ + if (cal_profile >= 0) { + next_profile = 0; + pr_debug("Loading %s profile! \n", tfaContProfileName(dev, next_profile)); + } + } + + for( dev=0; dev < devcount; dev++) { + /* check if the profile and steps are the one we want */ + /* was it not done already */ + if (( next_profile != active_profile && active_profile != -1) + || (istap_prof == 1)) { + err = tfaContWriteProfile(dev, next_profile, vstep[dev]); + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + } + + /* If the profile contains the .standby suffix go to powerdown + * else we should be in operating state + */ + if(strstr(tfaContProfileName(dev, next_profile), ".standby") != NULL) { + err = tfa98xx_powerdown(dev, 1); + } else if (TFA_GET_BF(dev, PWDN) != 0) { + err = tfa98xx_powerdown(dev, 0); + } + + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + + if (tfa98xx_runtime_verbose && tfa98xx_dev_family(dev) == 2) + err = show_current_state(dev); + + /* Always search and apply filters after a startup */ + err = tfa_set_filters(dev, next_profile); + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + +#ifdef __KERNEL__ + /* To write something in state 6 we need to be sure that SBSL is also set in IOMEM! + * More information can be found in the document about the powerswitch + */ + if (tfa98xx_dev_family(dev) == 2) { + /* If we are in state 0 or 1 we can skip setting SBSL and RST, but also filters and vstep (since there is no clk) + * We can be in state 0 or 1 because of standby profile or switching profile with internal clock + * No need to wait since we can only get out of state 0 or 1 by setting a bit (not by waiting) + */ + int manstate = TFA_GET_BF(dev, MANSTATE); + if (manstate > 1) { + /* Wait for the HW manager to be in state 6 (initCF) + * Absolute worstcase the HW manager takes 28ms (norm = ~3) + */ + while ((manstate < 6) && (kernel_sleep_timeout < 28)) { + kernel_sleep_timeout++; + msleep_interruptible(1); + manstate = TFA_GET_BF(dev, MANSTATE); + } + + if ((manstate == 6) && (TFA_GET_BF(dev, RST) == 1) && (TFA_GET_BF(dev, SBSL) == 0)) { + TFA_SET_BF_VOLATILE(dev, SBSL, 0); + TFA_SET_BF(dev, RST, 0); + } + } + } +#endif + + if (vstep[dev] != tfaContGetCurrentVstep(dev) && vstep[dev] != -1) { + err = tfaContWriteFilesVstep(dev, next_profile, vstep[dev]); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + + tfa_set_swprof(dev, (unsigned short)next_profile); + tfa_set_swvstep(dev, (unsigned short)tfaContGetCurrentVstep(dev)); + } + +error_exit: + if (tfa98xx_runtime_verbose && tfa98xx_dev_family(dev) == 2) + show_current_state(dev); + + for( dev=0; dev < devcount; dev++) { + tfaRunUnmute(dev); /* unmute */ + tfaContClose(dev); /* close all of them */ + } + + return err; +} + +enum tfa_error tfa_stop(void) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int dev, devcount = tfa98xx_cnt_max_device(); + + if ( devcount == 0 ) { + pr_err("No or wrong container file loaded\n"); + return tfa_error_bad_param; + } + + for( dev=0; dev < devcount; dev++) { + err = tfaContOpen(dev); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + if (tfa98xx_runtime_verbose) + pr_debug("Stopping device [%s]\n", tfaContDeviceName(dev)); + /* mute */ + tfaRunMute(dev); + /* powerdown CF */ + err = tfa98xx_powerdown(dev, 1 ); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + + /* disable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(dev, 0); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + +error_exit: + for( dev=0; dev < devcount; dev++) + tfaContClose(dev); /* close all of them */ + return err; +} + +/* + * int registers and coldboot dsp + */ +int tfa98xx_reset(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + TFA_SET_BF_VOLATILE(handle, I2CR, 1); + /* restore MANSCONF and MANCOLD to POR state */ + TFA_SET_BF_VOLATILE(handle, MANSCONF, 0); + TFA_SET_BF_VOLATILE(handle, MANCOLD, 1); + + /* powerup CF to access CF io */ + tfa98xx_powerdown(handle, 0 ); + /* for clock */ + err = tfa_cf_powerup(handle); + PRINT_ASSERT(err); + + /* force cold boot */ + err = tfaRunColdboot(handle, 1); // set ACS + PRINT_ASSERT(err); + + /* reset all i2C registers to default */ + err = -TFA_SET_BF(handle, I2CR, 1); + PRINT_ASSERT(err); + + return err; +} + +enum tfa_error tfa_reset(void) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int dev, devcount = tfa98xx_cnt_max_device(); + + for( dev=0; dev < devcount; dev++) { + err = tfaContOpen(dev); + if ( err != Tfa98xx_Error_Ok) + break; + if (tfa98xx_runtime_verbose) + pr_debug("resetting device [%s]\n", tfaContDeviceName(dev)); + err = tfa98xx_reset(dev); + if ( err != Tfa98xx_Error_Ok) + break; + } + + for( dev=0; dev < devcount; dev++) { + tfaContClose(dev); + } + + return err; +} + +/* + * Write all the bytes specified by num_bytes and data + */ +enum Tfa98xx_Error +tfa98xx_write_data(Tfa98xx_handle_t handle, + unsigned char subaddress, int num_bytes, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + /* subaddress followed by data */ + const int bytes2write = num_bytes + 1; + unsigned char *write_data; + + if (num_bytes > TFA2_MAX_PARAM_SIZE) + return Tfa98xx_Error_Bad_Parameter; + + write_data = (unsigned char *)kmalloc(bytes2write, GFP_KERNEL); + if (write_data == NULL) + return Tfa98xx_Error_Fail; + + write_data[0] = subaddress; + memcpy(&write_data[1], data, num_bytes); + + error = tfa98xx_write_raw(handle, bytes2write, write_data); + + kfree (write_data); + return error; +} + +/* + * fill the calibration value as milli ohms in the struct + * + * assume that the device has been calibrated + */ +enum Tfa98xx_Error +tfa_dsp_get_calibration_impedance(Tfa98xx_handle_t handle) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int spkr_count, nr_bytes, i; + unsigned char bytes[6] = {0}; + int data[2]; + + error = tfa98xx_supported_speakers(handle, &spkr_count); + if (error == Tfa98xx_Error_Ok) { + /* If calibrate=once then get values from MTP */ + if (TFA_GET_BF(handle, MTPOTC) && ((handles_local[handle].rev & 0xff) == 0x88)) { + if (tfa98xx_runtime_verbose) + pr_debug("Getting calibration values from MTP\n"); + for(i=0; i 1 ) { + TFA_SET_BF_VOLATILE(handle, SWPROFIL, new_value+1); + } else { + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(handle, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(handle, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(handle, SWPROFIL, new_value+1); /* set current profile */ + TFA_SET_BF_VOLATILE(handle, MTPK, (uint16_t)mtpk); /* restore key */ + } + + return active_value; +} + +/* same value for all channels + * start count from 1, 0 is invalid */ +int tfa_get_swvstep(Tfa98xx_handle_t handle){ + int value; + + if ( handles_local[handle].vstep[0]>0) + return handles_local[handle].vstep[0]-1; + + value = TFA_GET_BF(handle, SWVSTEP); /* get current vstep[0] */ + + handles_local[handle].vstep[0] = value; + handles_local[handle].vstep[1] = value; + return value-1; /* invalid if 0 */ + +} +int tfa_set_swvstep(Tfa98xx_handle_t handle, unsigned short new_value) { + int mtpk, active_value = tfa_get_swvstep(handle); + + handles_local[handle].vstep[0]=new_value; + handles_local[handle].vstep[1]=new_value; + + if ( handles_local[handle].tfa_family > 1 ) { + TFA_SET_BF_VOLATILE(handle, SWVSTEP, new_value+1); + } else { + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(handle, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(handle, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(handle, SWVSTEP, new_value+1); /* set current vstep[0] */ + TFA_SET_BF_VOLATILE(handle, MTPK, (uint16_t)mtpk); /* restore key */ + } + + return active_value; +} diff --git a/sound/soc/codecs/tfa9891/tfa_dsp_fw.h b/sound/soc/codecs/tfa9891/tfa_dsp_fw.h new file mode 100755 index 000000000000..7d3829af3b93 --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_dsp_fw.h @@ -0,0 +1,119 @@ +#ifndef TFA98XX_INTERNALS_H +#define TFA98XX_INTERNALS_H + +#include "tfa_service.h" + +/* + * the order matches the ACK bits order in TFA98XX_CF_STATUS + */ +enum tfa_fw_event { /* not all available on each device */ + tfa_fw_i2c_cmd_ack, + tfa_fw_reset_start, + tfa_fw_short_on_mips, + tfa_fw_soft_mute_ready, + tfa_fw_volume_ready, + tfa_fw_error_damage, + tfa_fw_calibrate_done, + tfa_fw_max +}; + +/* the following type mappings are compiler specific */ +#define subaddress_t unsigned char + +/* module Ids */ +#define MODULE_FRAMEWORK 0 +#define MODULE_SPEAKERBOOST 1 +#define MODULE_BIQUADFILTERBANK 2 +#define MODULE_SETRE 9 + +/* RPC commands */ +/* SET */ +#define FW_PAR_ID_SET_MEMORY 0x03 +#define FW_PAR_ID_SET_SENSES_DELAY 0x04 +#define FW_PAR_ID_SETSENSESCAL 0x05 +#define FW_PAR_ID_SET_INPUT_SELECTOR 0x06 +#define FW_PAR_ID_SET_OUTPUT_SELECTOR 0x08 +#define FW_PAR_ID_SET_PROGRAM_CONFIG 0x09 +#define FW_PAR_ID_SET_GAINS 0x0A +#define FW_PAR_ID_SET_MEMTRACK 0x0B +#define TFA1_FW_PAR_ID_SET_CURRENT_DELAY 0x03 +#define TFA1_FW_PAR_ID_SET_CURFRAC_DELAY 0x06 +/* GET */ +#define FW_PAR_ID_GET_MEMORY 0x83 +#define FW_PAR_ID_GLOBAL_GET_INFO 0x84 +#define FW_PAR_ID_GET_FEATURE_INFO 0x85 +#define FW_PAR_ID_GET_MEMTRACK 0x8B +#define FW_PAR_ID_GET_TAG 0xFF + +/* Load a full model into SpeakerBoost. */ +/* SET */ +#define SB_PARAM_SET_ALGO_PARAMS 0x00 +#define SB_PARAM_SET_LAGW 0x01 +#define SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET 0x02 +#define SB_PARAM_SET_LSMODEL 0x06 +#define SB_PARAM_SET_MBDRC 0x07 +#define SB_PARAM_SET_MBDRC_WITHOUT_RESET 0x08 +#define SB_PARAM_SET_DRC 0x0F +/* GET */ +#define SB_PARAM_GET_ALGO_PARAMS 0x80 +#define SB_PARAM_GET_LAGW 0x81 +#define SB_PARAM_GET_RE0 0x85 +#define SB_PARAM_GET_LSMODEL 0x86 +#define SB_PARAM_GET_MBDRC 0x87 +#define SB_PARAM_GET_MBDRC_DYNAMICS 0x89 +#define SB_PARAM_GET_TAG 0xFF + +#define SB_PARAM_SET_EQ 0x0A /* 2 Equaliser Filters. */ +#define SB_PARAM_SET_PRESET 0x0D /* Load a preset */ +#define SB_PARAM_SET_CONFIG 0x0E /* Load a config */ +#define SB_PARAM_SET_AGCINS 0x10 +#define SB_PARAM_SET_CURRENT_DELAY 0x03 +#define SB_PARAM_GET_STATE 0xC0 +#define SB_PARAM_GET_XMODEL 0xC1 /* Gets current Excursion Model. */ + +/* sets the speaker calibration impedance (@25 degrees celsius) */ +#define SB_PARAM_SET_RE0 0x89 + +#define BFB_PAR_ID_SET_COEFS 0x00 +#define BFB_PAR_ID_GET_COEFS 0x80 +#define BFB_PAR_ID_GET_CONFIG 0x81 + +/* for compatibility */ +#define FW_PARAM_GET_STATE FW_PAR_ID_GLOBAL_GET_INFO +#define FW_PARAM_GET_FEATURE_BITS FW_PAR_ID_GET_FEATURE_BITS + + +/* RPC Status results */ +#define STATUS_OK 0 +#define STATUS_INVALID_MODULE_ID 2 +#define STATUS_INVALID_PARAM_ID 3 +#define STATUS_INVALID_INFO_ID 4 + +/* the maximum message length in the communication with the DSP */ +#define TFA2_MAX_PARAM_SIZE (507*3) /* TFA2 */ +#define TFA1_MAX_PARAM_SIZE (145*3) /* TFA1 */ + +#define ROUND_DOWN(a,n) (((a)/(n))*(n)) + +/* feature bits */ +#define FEATURE1_TCOEF 0x100 /* bit8 set means tCoefA expected */ +#define FEATURE1_DRC 0x200 /* bit9 NOT set means DRC expected */ + +/* DSP firmware xmem defines */ +#define TFA1_FW_XMEM_CALIBRATION_DONE 231 +#define TFA2_FW_XMEM_CALIBRATION_DONE 516 +#define TFA1_FW_XMEM_COUNT_BOOT 0xa1 +#define TFA2_FW_XMEM_COUNT_BOOT 512 +#define TFA2_FW_XMEM_CMD_COUNT 520 + +/* note that the following defs rely on the handle variable */ +#define TFA_FW_XMEM_CALIBRATION_DONE TFA_FAM_FW(handle,XMEM_CALIBRATION_DONE) +#define TFA_FW_XMEM_COUNT_BOOT TFA_FAM_FW(handle,XMEM_COUNT_BOOT) +#define TFA_FW_XMEM_CMD_COUNT TFA_FAM_FW(handle,XMEM_CMD_COUNT) + +#define TFA2_FW_ReZ_SCALE 65536 +#define TFA1_FW_ReZ_SCALE 16384 +#define TFA_FW_ReZ_SCALE TFA_FAM_FW(handle,ReZ_SCALE) + + +#endif /* TFA98XX_INTERNALS_H */ diff --git a/sound/soc/codecs/tfa9891/tfa_hal.c b/sound/soc/codecs/tfa9891/tfa_hal.c new file mode 100755 index 000000000000..e4fbf6d0458b --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_hal.c @@ -0,0 +1,123 @@ +/* + *Copyright 2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#include +#include + +#include "tfa_dsp_fw.h" +#include "dbgprint.h" + +#include "NXP_I2C.h" +#include "tfa_internal.h" + + +/* translate a I2C driver error into an error for Tfa9887 API */ +static enum Tfa98xx_Error tfa98xx_classify_i2c_error(enum NXP_I2C_Error i2c_error) +{ + switch (i2c_error) { + case NXP_I2C_Ok: + return Tfa98xx_Error_Ok; + case NXP_I2C_NoAck: + case NXP_I2C_ArbLost: + case NXP_I2C_TimeOut: + return Tfa98xx_Error_I2C_NonFatal; + default: + return Tfa98xx_Error_I2C_Fatal; + } +} +/* + * write a 16 bit subaddress + */ +enum Tfa98xx_Error +tfa98xx_write_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, unsigned short value) +{ + enum NXP_I2C_Error i2c_error; + unsigned char write_data[3]; /* subaddress and 2 bytes of the value */ + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + + write_data[0] = subaddress; + write_data[1] = (value >> 8) & 0xFF; + write_data[2] = value & 0xFF; + + i2c_error = NXP_I2C_WriteRead(handles_local[handle].slave_address, sizeof(write_data), write_data, 0, NULL); + + return tfa98xx_classify_i2c_error(i2c_error); +} + +enum Tfa98xx_Error +tfa98xx_read_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, unsigned short *pValue) +{ + enum NXP_I2C_Error i2c_error; + unsigned char write_data[1]; /* subaddress */ + unsigned char read_buffer[2]; /* 2 data bytes */ + + _ASSERT(pValue != NULL); + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + write_data[0] = subaddress; + read_buffer[0] = read_buffer[1] = 0; + + i2c_error = NXP_I2C_WriteRead(handles_local[handle].slave_address, + sizeof(write_data), write_data, sizeof(read_buffer), read_buffer); + if (tfa98xx_classify_i2c_error(i2c_error) != Tfa98xx_Error_Ok) { + return tfa98xx_classify_i2c_error(i2c_error); + } else { + *pValue = (read_buffer[0] << 8) + read_buffer[1]; + return Tfa98xx_Error_Ok; + } +} + +enum Tfa98xx_Error +tfa98xx_read_data(Tfa98xx_handle_t handle, + unsigned char subaddress, int num_bytes, unsigned char data[]) +{ + enum NXP_I2C_Error i2c_error; + unsigned char write_data[1]; /* subaddress */ + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + if (num_bytes > handles_local[handle].buffer_size) + return Tfa98xx_Error_Bad_Parameter; + + write_data[0] = subaddress; + i2c_error = + NXP_I2C_WriteRead(handles_local[handle].slave_address, sizeof(write_data), + write_data, num_bytes, data); + return tfa98xx_classify_i2c_error(i2c_error); +} + +/* + * Write raw I2C data with no sub address + */ +enum Tfa98xx_Error +tfa98xx_write_raw(Tfa98xx_handle_t handle, + int num_bytes, + const unsigned char data[]) +{ + enum NXP_I2C_Error i2c_error; + + if (!tfa98xx_handle_is_open(handle)) + return Tfa98xx_Error_NotOpen; + if (num_bytes > handles_local[handle].buffer_size) + return Tfa98xx_Error_Bad_Parameter; + i2c_error = + NXP_I2C_WriteRead(handles_local[handle].slave_address, num_bytes, + data, 0, NULL); + return tfa98xx_classify_i2c_error(i2c_error); +} diff --git a/sound/soc/codecs/tfa9891/tfa_internal.h b/sound/soc/codecs/tfa9891/tfa_internal.h new file mode 100755 index 000000000000..82ff114d86de --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_internal.h @@ -0,0 +1,115 @@ +/* + internal functions for TFA layer (not shared with SRV and HAL layer!) +*/ + +#ifndef __TFA_INTERNAL_H__ +#define __TFA_INTERNAL_H__ + +#include "tfa_dsp_fw.h" +#include "tfa_service.h" +#include "config.h" + +#if __GNUC__ >= 4 + #define TFA_INTERNAL __attribute__ ((visibility ("hidden"))) +#else + #define TFA_INTERNAL +#endif + + +#define TFA98XX_GENERIC_SLAVE_ADDRESS 0x1C + +enum featureSupport { + supportNotSet, /* the default is not set yet, so = 0 */ + supportNo, + supportYes +}; + +typedef enum featureSupport featureSupport_t; + +/* + * tfa98xx control structure gathers data related to a single control + * (a 'control' can be related to an interface file) + * Some operations can be flagged as deferrable, meaning that they can be + * scheduled for later execution. This can be used for operations that + * require the i2s clock to run, and if it is not available while applying + * the control. + * The Some operations can as well be cache-able (supposedly they are the same + * operations as the deferrable). Cache-able means that the status or + * register value may not be accesable while accessing the control. Caching + * allows to get the last programmed value. + * + * Fields: + * deferrable: + * true: means the action or register accces can be run later (for example + * when an active i2s clock will be available). + * false: meams the operation can be applied immediately + * triggered: true if the deferred operation was triggered and is scheduled + * to run later + * wr_value: the value to write in the deferred action (if applicable) + * rd_value: the cached value to report on a cached read (if applicable) + * rd_valid: true if the rd_value was initialized (and can be reported) + */ + +struct tfa98xx_control { + bool deferrable; + bool triggered; + int wr_value; + int rd_value; + bool rd_valid; +}; + +struct tfa98xx_controls { + struct tfa98xx_control otc; + struct tfa98xx_control mtpex; + struct tfa98xx_control calib; +// struct tfa98xx_control r; +// struct tfa98xx_control temp; +}; + +struct tfa_device_ops { + enum Tfa98xx_Error (*tfa_init)(Tfa98xx_handle_t dev_idx); + enum Tfa98xx_Error (*tfa_dsp_reset)(Tfa98xx_handle_t dev_idx, int state); + enum Tfa98xx_Error (*tfa_dsp_system_stable)(Tfa98xx_handle_t handle, int *ready); + enum Tfa98xx_Error (*tfa_dsp_write_tables)(Tfa98xx_handle_t dev_idx, int sample_rate); + struct tfa98xx_controls controls; +}; + +struct Tfa98xx_handle_private { + int in_use; + int buffer_size; + unsigned char slave_address; + unsigned short rev; + unsigned char tfa_family; /* tfa1/tfa2 */ + enum featureSupport supportDrc; + enum featureSupport supportFramework; + enum featureSupport support_saam; + int sw_feature_bits[2]; /* cached feature bits data */ + int hw_feature_bits; /* cached feature bits data */ + int profile; /* cached active profile */ + int vstep[2]; /* cached active vsteps */ +// TODO add? unsigned char rev_major; /* void tfa98xx_rev(int *major, int *minor, int *revision)*/ +// unsigned char rev_minor; +// unsigned char rev_build; + unsigned char spkr_count; + unsigned char spkr_select; + unsigned char support_tcoef; + enum Tfa98xx_DAI daimap; + int mohm[3]; /* > speaker calibration values in milli ohms -1 is error */ + struct tfa_device_ops dev_ops; + uint16_t interrupt_enable[3]; + uint16_t interrupt_status[3]; +}; + +/* tfa_core.c */ +extern TFA_INTERNAL struct Tfa98xx_handle_private handles_local[]; +TFA_INTERNAL int tfa98xx_handle_is_open(Tfa98xx_handle_t h); +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_check_rpc_status(Tfa98xx_handle_t handle, int *pRpcStatus); +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_wait_result(Tfa98xx_handle_t handle, int waitRetryCount); +TFA_INTERNAL void tfa98xx_apply_deferred_calibration(Tfa98xx_handle_t handle); +TFA_INTERNAL void tfa98xx_deferred_calibration_status(Tfa98xx_handle_t handle, int calibrateDone); +TFA_INTERNAL int print_calibration(Tfa98xx_handle_t handle, char *str, size_t size); +/*wangdongdong@MultiMedia,2016/11/30,add for calibrate node*/ +TFA_INTERNAL int print_calibration_modify(Tfa98xx_handle_t handle, char *str, size_t size); + +#endif /* __TFA_INTERNAL_H__ */ + diff --git a/sound/soc/codecs/tfa9891/tfa_osal.c b/sound/soc/codecs/tfa9891/tfa_osal.c new file mode 100755 index 000000000000..8f7adc67fc6e --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_osal.c @@ -0,0 +1,40 @@ +#include "config.h" + +#if (defined(WIN32) || defined(_X64)) +#include +#else +#include +#endif + +#include "tfa_internal.h" + +void *kmalloc(size_t size, gfpt_t flags) +{ + /* flags are not used outside the Linux kernel */ + (void)flags; + +#if !defined(__REDLIB__) + return malloc(size); +#else + // TODO !need malloc here +#endif +} + +void kfree(const void *ptr) +{ +#if !defined(__REDLIB__) + free((void *)ptr); +#else + // TODO !need free here +#endif +} + +unsigned long msleep_interruptible(unsigned int msecs) +{ +#if (defined(WIN32) || defined(_X64)) + Sleep(msecs); +#else + usleep(1000 * msecs); +#endif + return 0; +} diff --git a/sound/soc/codecs/tfa9891/tfa_service.h b/sound/soc/codecs/tfa9891/tfa_service.h new file mode 100755 index 000000000000..4a48555efdae --- /dev/null +++ b/sound/soc/codecs/tfa9891/tfa_service.h @@ -0,0 +1,1010 @@ +/* + *Copyright 2015 NXP Semiconductors + * + *Licensed under the Apache License, Version 2.0 (the "License"); + *you may not use this file except in compliance with the License. + *You may obtain a copy of the License at + * + *http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, software + *distributed under the License is distributed on an "AS IS" BASIS, + *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *See the License for the specific language governing permissions and + *limitations under the License. + */ + +#ifndef TFA_SERVICE_H +#define TFA_SERVICE_H + +//#include "config.h" +// workaround for Visual Studio: +// fatal error C1083: Cannot open include file: 'config.h': No such file or directory +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#include "NXP_I2C.h" +#endif + +#define TFA98XX_API_REV_MAJOR (2) /* major API rev */ +#define TFA98XX_API_REV_MINOR (10) /* minor */ +#define TFA98XX_API_REV_REVISION (2) +#define TFA98XX_API_REV_STR "2.10.2" + +/* + * data previously defined in Tfa9888_dsp.h + */ +#define MEMTRACK_MAX_WORDS 50 +#define LSMODEL_MAX_WORDS 150 +#define TFA98XX_MAXTAG (138) +#define FW_VAR_API_VERSION (521) + +// these indexes and scaling factors should be recovered from the ITF xml file. +#define fs_IDX 128 +#define leakageFactor_IDX 130 +#define ReCorrection_IDX 131 +#define Bl_IDX 132 +#define tCoef_IDX 138 +#define ReZ_IDX 147 + +#define fs_SCALE (double)1 +#define leakageFactor_SCALE (double)8388608 +#define ReCorrection_SCALE (double)8388608 +#define Bl_SCALE (double)2097152 +#define tCoef_SCALE (double)8388608 + +/* ---------------------------- Max1 ---------------------------- */ +/* Headroom applied to the main input signal */ +#define SPKRBST_HEADROOM 7 +/* Exponent used for AGC Gain related variables */ +#define SPKRBST_AGCGAIN_EXP SPKRBST_HEADROOM +#define SPKRBST_TEMPERATURE_EXP 9 +/* Exponent used for Gain Corection related variables */ +#define SPKRBST_LIMGAIN_EXP 4 +#define SPKRBST_TIMECTE_EXP 1 +#define DSP_MAX_GAIN_EXP 7 +/* -------------------------------------------------------------- */ + +/* speaker related parameters */ +#define TFA2_SPEAKERPARAMETER_LENGTH (3*151) /* MAX2=450 */ +#define TFA1_SPEAKERPARAMETER_LENGTH (3*141) /* MAX1=423 */ + +/* vstep related parameters */ +#define TFA2_ALGOPARAMETER_LENGTH (3*304) /* N1B = (304) 305 is including the cmd-id */ +#define TFA2_MBDRCPARAMETER_LENGTH (3*152) /* 154 is including the cmd-id */ +#define TFA1_PRESET_LENGTH 87 +#define TFA1_DRC_LENGTH 381 /* 127 words */ +#define TFA2_FILTERCOEFSPARAMETER_LENGTH (3*168) /* 170 is including the cmd-id */ + +/* Maximum number of retries for DSP result + * Keep this value low! + * If certain calls require longer wait conditions, the + * application should poll, not the API + * The total wait time depends on device settings. Those + * are application specific. + */ +#define TFA98XX_WAITRESULT_NTRIES 40 +#define TFA98XX_WAITRESULT_NTRIES_LONG 2000 + +/* following lengths are in bytes */ +#define TFA98XX_PRESET_LENGTH 87 + +/* make full the default */ +#if !(defined(TFA9887) || defined(TFA9890) || defined(TFA9887B) || defined(TFA9897)) +#ifndef TFA98XX_FULL +#define TFA98XX_FULL +#endif +#endif + +#if (defined(TFA9887) || defined(TFA9890) || defined(TFA9897)) +#define TFA98XX_CONFIG_LENGTH 165 +#else +#if (defined(TFA9887B) || defined(TFA98XX_FULL)) +#define TFA98XX_CONFIG_LENGTH 201 +#define TFA98XX_DRC_LENGTH 381 /* 127 words */ + typedef unsigned char Tfa98xx_DrcParameters_t[TFA98XX_DRC_LENGTH]; +#endif +#endif + +/* +MUST BE CONSISTANT: either one uses opaque arrays of bytes, or not!!! +*/ +typedef unsigned char Tfa98xx_Config_t[TFA98XX_CONFIG_LENGTH]; +typedef unsigned char Tfa98xx_Preset_t[TFA98XX_PRESET_LENGTH]; + +/* Type containing all the possible errors that can occur + * + */ +enum Tfa98xx_Error { + Tfa98xx_Error_Ok = 0, + Tfa98xx_Error_Device, /* Currently only used to keep in sync with tfa_error */ + Tfa98xx_Error_Bad_Parameter, + Tfa98xx_Error_Fail, /* generic failure, avoid mislead message */ + Tfa98xx_Error_NoClock, /* no clock detected */ + Tfa98xx_Error_StateTimedOut, + Tfa98xx_Error_DSP_not_running, /* communication with the DSP failed */ + Tfa98xx_Error_AmpOn, /* amp is still running */ + Tfa98xx_Error_NotOpen, /* the given handle is not open */ + Tfa98xx_Error_InUse, /* too many handles */ + Tfa98xx_Error_Buffer_too_small, /* if a buffer is too small */ + /* the expected response did not occur within the expected time */ + Tfa98xx_Error_RpcBase = 100, + Tfa98xx_Error_RpcBusy = 101, + Tfa98xx_Error_RpcModId = 102, + Tfa98xx_Error_RpcParamId = 103, + Tfa98xx_Error_RpcInfoId = 104, + Tfa98xx_Error_RpcNotAllowedSpeaker = 105, + + Tfa98xx_Error_Not_Implemented, + Tfa98xx_Error_Not_Supported, + Tfa98xx_Error_I2C_Fatal, /* Fatal I2C error occurred */ + /* Nonfatal I2C error, and retry count reached */ + Tfa98xx_Error_I2C_NonFatal, + Tfa98xx_Error_Other = 1000 +}; + +/* + * Type containing all the possible msg returns DSP can give + */ +enum Tfa98xx_Status_ID { + Tfa98xx_DSP_Not_Running = -1, /* No response from DSP */ + Tfa98xx_I2C_Req_Done = 0, /* Request executed correctly and result, if any, is available for download */ + Tfa98xx_I2C_Req_Busy = 1, /* Request is being processed, just wait for result */ + Tfa98xx_I2C_Req_Invalid_M_ID = 2, /* Provided M-ID does not fit in valid rang [0..2] */ + Tfa98xx_I2C_Req_Invalid_P_ID = 3, /* Provided P-ID isn�t valid in the given M-ID context */ + Tfa98xx_I2C_Req_Invalid_CC = 4, /* Invalid channel configuration bits (SC|DS|DP|DC) combination */ + Tfa98xx_I2C_Req_Invalid_Seq = 5, /* Invalid sequence of commands, in case the DSP expects some commands in a specific order */ + Tfa98xx_I2C_Req_Invalid_Param = 6, /* Generic error */ + Tfa98xx_I2C_Req_Buffer_Overflow = 7 /* I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed */ +}; + +/* + * speaker as microphone + */ +enum Tfa98xx_saam { + Tfa98xx_saam_none, /*< SAAM feature not available */ + Tfa98xx_saam /*< SAAM feature available */ +}; + +/* + * possible Digital Audio Interfaces bitmap + */ +enum Tfa98xx_DAI { + Tfa98xx_DAI_I2S = 0x01, + Tfa98xx_DAI_TDM = 0x02, + Tfa98xx_DAI_PDM = 0x04, +}; + +/* + * config file subtypes + */ +enum Tfa98xx_config_type { + Tfa98xx_config_generic, + Tfa98xx_config_sub1, + Tfa98xx_config_sub2, + Tfa98xx_config_sub3, +}; + +enum Tfa98xx_AmpInputSel { + Tfa98xx_AmpInputSel_I2SLeft, + Tfa98xx_AmpInputSel_I2SRight, + Tfa98xx_AmpInputSel_DSP +}; + +enum Tfa98xx_OutputSel { + Tfa98xx_I2SOutputSel_CurrentSense, + Tfa98xx_I2SOutputSel_DSP_Gain, + Tfa98xx_I2SOutputSel_DSP_AEC, + Tfa98xx_I2SOutputSel_Amp, + Tfa98xx_I2SOutputSel_DataI3R, + Tfa98xx_I2SOutputSel_DataI3L, + Tfa98xx_I2SOutputSel_DcdcFFwdCur, +}; + +enum Tfa98xx_StereoGainSel { + Tfa98xx_StereoGainSel_Left, + Tfa98xx_StereoGainSel_Right +}; + +#define TFA98XX_MAXPATCH_LENGTH (3*1024) + +/* the number of biquads supported */ +#define TFA98XX_BIQUAD_NUM 10 + +enum Tfa98xx_Channel { + Tfa98xx_Channel_L, + Tfa98xx_Channel_R, + Tfa98xx_Channel_L_R, + Tfa98xx_Channel_Stereo +}; + +enum Tfa98xx_Mode { + Tfa98xx_Mode_Normal = 0, + Tfa98xx_Mode_RCV +}; + +enum Tfa98xx_Mute { + Tfa98xx_Mute_Off, + Tfa98xx_Mute_Digital, + Tfa98xx_Mute_Amplifier +}; + +enum Tfa98xx_SpeakerBoostStatusFlags { + Tfa98xx_SpeakerBoost_Activity = 0, /* Input signal activity. */ + Tfa98xx_SpeakerBoost_S_Ctrl, /* S Control triggers the limiter */ + Tfa98xx_SpeakerBoost_Muted, /* 1 when signal is muted */ + Tfa98xx_SpeakerBoost_X_Ctrl, /* X Control triggers the limiter */ + Tfa98xx_SpeakerBoost_T_Ctrl, /* T Control triggers the limiter */ + Tfa98xx_SpeakerBoost_NewModel, /* New model is available */ + Tfa98xx_SpeakerBoost_VolumeRdy, /* 0:stable vol, 1:still smoothing */ + Tfa98xx_SpeakerBoost_Damaged, /* Speaker Damage detected */ + Tfa98xx_SpeakerBoost_SignalClipping /* input clipping detected */ +}; + +struct Tfa98xx_DrcStateInfo { + float GRhighDrc1[2]; + float GRhighDrc2[2]; + float GRmidDrc1[2]; + float GRmidDrc2[2]; + float GRlowDrc1[2]; + float GRlowDrc2[2]; + float GRpostDrc1[2]; + float GRpostDrc2[2]; + float GRblDrc[2]; +}; +struct Tfa98xx_StateInfo { + /* SpeakerBoost State */ + float agcGain; /* Current AGC Gain value */ + float limGain; /* Current Limiter Gain value */ + float sMax; /* Current Clip/Lim threshold */ + int T; /* Current Speaker Temperature value */ + int statusFlag; /* Masked bit word */ + float X1; /* estimated excursion caused by Spkrboost gain ctrl */ + float X2; /* estimated excursion caused by manual gain setting */ + float Re; /* Loudspeaker blocked resistance */ + /* Framework state */ + /* increments each time a MIPS problem is detected on the DSP */ + int shortOnMips; + /* DRC state, when enabled */ + struct Tfa98xx_DrcStateInfo drcState; +}; + +typedef struct nxpTfaMsg { + uint8_t msg_size; + unsigned char cmdId[3]; + int data[9]; +} nxpTfaMsg_t; + +typedef struct nxpTfaGroup { + uint8_t msg_size; + uint8_t profileId[64]; +} nxpTfaGroup_t; + + +struct nxpTfa98xx_Memtrack_data { + int length; + float mValues[MEMTRACK_MAX_WORDS]; + int mAdresses[MEMTRACK_MAX_WORDS]; + int scalingFactor[MEMTRACK_MAX_WORDS]; + int trackers[MEMTRACK_MAX_WORDS]; +}; + +/* possible memory values for DMEM in CF_CONTROLs */ +enum Tfa98xx_DMEM { + Tfa98xx_DMEM_PMEM = 0, + Tfa98xx_DMEM_XMEM = 1, + Tfa98xx_DMEM_YMEM = 2, + Tfa98xx_DMEM_IOMEM = 3, +}; + +/** + * lookup the device type and return the family type + */ +int tfa98xx_dev2family(int dev_type); + +/** + * register definition structure + */ +struct regdef { + unsigned char offset; /**< subaddress offset */ + unsigned short pwronDefault; + /**< register contents after poweron */ + unsigned short pwronTestmask; + /**< mask of bits not test */ + char *name; /**< short register name */ +}; + +#define Tfa98xx_handle_t int + +/** + * Open the instance handle + */ +enum Tfa98xx_Error tfa98xx_open(Tfa98xx_handle_t handle); + +/** + * Load the default HW settings in the device + */ +enum Tfa98xx_Error tfa98xx_init(Tfa98xx_handle_t handle); + +/** + * Return the tfa revision + */ +void tfa98xx_rev(int *major, int *minor, int *revision); + +enum Tfa98xx_DMEM tfa98xx_filter_mem(Tfa98xx_handle_t dev, + int filter_index, unsigned short *address, int channel); + +/** + * Return the maximum nr of devices + */ +int tfa98xx_max_devices(void); + +/** + * If needed, this function can be used to get a text version of the status ID code + * @param the given status ID code + * @return the I2C status ID string + */ +const char *tfa98xx_get_i2c_status_id_string(int status); + +/** + * Close the instance handle + */ +enum Tfa98xx_Error tfa98xx_close(Tfa98xx_handle_t handle); + +/* control the powerdown bit of the TFA9887 + * @param powerdown must be 1 or 0 + */ +enum Tfa98xx_Error tfa98xx_powerdown(Tfa98xx_handle_t handle, + int powerdown); + +/* control the input_sel bits of the TFA9887, to indicate */ +/* what is sent to the amplfier and speaker + * @param input_sel, see Tfa98xx_AmpInputSel_t + */ +enum Tfa98xx_Error tfa98xx_select_amplifier_input(Tfa98xx_handle_t handle, + enum Tfa98xx_AmpInputSel + input_sel); + +/* control the I2S left output of the TFA9887 + * @param output_sel, see Tfa98xx_OutputSel_t + */ +enum Tfa98xx_Error tfa98xx_select_i2s_output_left(Tfa98xx_handle_t handle, + enum Tfa98xx_OutputSel + output_sel); + +/* control the I2S right output of the TFA9887 + * @param output_sel, see Tfa98xx_OutputSel_t + */ +enum Tfa98xx_Error tfa98xx_select_i2s_output_right(Tfa98xx_handle_t handle, + enum Tfa98xx_OutputSel + output_sel); + +/* indicates on which channel of DATAI2 the gain from the IC is set + * @param gain_sel, see Tfa98xx_StereoGainSel_t + */ +enum Tfa98xx_Error tfa98xx_select_stereo_gain_channel(Tfa98xx_handle_t handle, + enum Tfa98xx_StereoGainSel + gain_sel); + +/* TODO cleanup calibration support */ + +/** + * set the mtp with user controllable values + * @param value to be written + * @param mask to be applied toi the bits affected + */ +enum Tfa98xx_Error tfa98xx_set_mtp(Tfa98xx_handle_t handle, uint16_t value, uint16_t mask); + +enum Tfa98xx_Error tfa98xx_get_mtp(Tfa98xx_handle_t handle, uint16_t *value); + +/** + * lock or unlock KEY2 + * lock = 1 will lock + * lock = 0 will unlock + * note that on return all the hidden key will be off + */ +void tfa98xx_key2(Tfa98xx_handle_t handle, int lock); + +int tfa_calibrate(Tfa98xx_handle_t handle) ; +void tfa98xx_set_exttemp(Tfa98xx_handle_t handle, short ext_temp); +short tfa98xx_get_exttemp(Tfa98xx_handle_t handle); + +/* control the volume of the DSP + * @param vol volume in bit field. It must be between 0 and 255 + */ +enum Tfa98xx_Error tfa98xx_set_volume_level(Tfa98xx_handle_t handle, + unsigned short vol); + +/* read the TFA9887 of the sample rate of the I2S bus that will be used. + * @param pRate pointer to rate in Hz i.e 32000, 44100 or 48000 + */ +enum Tfa98xx_Error tfa98xx_get_sample_rate(Tfa98xx_handle_t handle, + int *pRate); + +/* set the input channel to use + * @param channel see Tfa98xx_Channel_t enumeration + */ +enum Tfa98xx_Error tfa98xx_select_channel(Tfa98xx_handle_t handle, + enum Tfa98xx_Channel channel); + +/* set the mode for normal or receiver mode + * @param mode see Tfa98xx_Mode enumeration + */ +enum Tfa98xx_Error tfa98xx_select_mode(Tfa98xx_handle_t handle, enum Tfa98xx_Mode mode ); + +/* mute/unmute the audio + * @param mute see Tfa98xx_Mute_t enumeration + */ +enum Tfa98xx_Error tfa98xx_set_mute(Tfa98xx_handle_t handle, + enum Tfa98xx_Mute mute); + +/* + * tfa98xx_supported_speakers - required for SmartStudio initialization + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa98xx_supported_speakers(Tfa98xx_handle_t handle, int* spkr_count); + +/* + * Return the feature bits from MTP and cnt file for comparison + */ +enum Tfa98xx_Error +tfa98xx_compare_features(Tfa98xx_handle_t handle, int features_from_MTP[3], int features_from_cnt[3]); + +/* + * return feature bits + */ +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(Tfa98xx_handle_t handle, int features[2]); +enum Tfa98xx_Error +tfa98xx_dsp_get_hw_feature_bits(Tfa98xx_handle_t handle, int *features); +/* + * tfa98xx_supported_dai + * returns the bitmap of the supported Digital Audio Interfaces + * @param dai bitmap enum pointer + * @return error code + */ +enum Tfa98xx_Error tfa98xx_supported_dai(Tfa98xx_handle_t handle, enum Tfa98xx_DAI *daimap); + +/* + * tfa98xx_supported_saam + * returns the speaker as microphone feature + * @param saam enum pointer + * @return error code + */ +enum Tfa98xx_Error tfa98xx_supported_saam(Tfa98xx_handle_t handle, enum Tfa98xx_saam *saam); + +/* load the tables to the DSP + * called after patch load is done + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_write_tables(Tfa98xx_handle_t handle, int sample_rate); + +/* set or clear DSP reset signal + * @param new state + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_reset(Tfa98xx_handle_t handle, int state); + +/* check the state of the DSP subsystem + * return ready = 1 when clocks are stable to allow safe DSP subsystem access + * @param pointer to state flag, non-zero if clocks are not stable + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(Tfa98xx_handle_t handle, + int *ready); + +/** + * check the state of the DSP coolflux + * returns the value of CFE + */ +int tfa98xx_cf_enabled(Tfa98xx_handle_t dev_idx); + +/* The following functions can only be called when the DSP is running + * - I2S clock must be active, + * - IC must be in operating mode + */ + +/** + * patch the ROM code of the DSP + * @param handle to opened instance + * @param patchLength the number of bytes of patchBytes + * @param patchBytes pointer to the bytes to patch + */ +enum Tfa98xx_Error tfa_dsp_patch(Tfa98xx_handle_t handle, + int patchLength, + const unsigned char *patchBytes); + +/* Check whether the DSP expects tCoef or tCoefA as last parameter in + * the speaker parameters + * *pbSupporttCoef=1 when DSP expects tCoef, + * *pbSupporttCoef=0 when it expects tCoefA (and the elaborate workaround + * to calculate tCoefA from tCoef on the host) + */ +enum Tfa98xx_Error tfa98xx_dsp_support_tcoef(Tfa98xx_handle_t handle, + int *pbSupporttCoef); + +/** + * return the tfa device family id + */ +int tfa98xx_dev_family(Tfa98xx_handle_t dev_idx); + +/** + * return the device revision id + */ +unsigned short tfa98xx_dev_revision(Tfa98xx_handle_t dev_idx); + +/** + * load explicitly the speaker parameters in case of free speaker, + * or when using a saved speaker model + */ +enum Tfa98xx_Error tfa98xx_dsp_write_speaker_parameters( + Tfa98xx_handle_t handle, + int length, + const unsigned char *pSpeakerBytes); + +/** + * read the speaker parameters as used by the SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_speaker_parameters( + Tfa98xx_handle_t handle, + int length, + unsigned char *pSpeakerBytes); + +/** + * read the current status of the DSP, typically used for development, + * not essential to be used in a product + */ +enum Tfa98xx_Error tfa98xx_dsp_get_state_info( + Tfa98xx_handle_t handle, + unsigned char bytes[], + unsigned int *statesize); + +/** + * Check whether the DSP supports DRC + * pbSupportDrc=1 when DSP supports DRC, + * pbSupportDrc=0 when DSP doesn't support it + */ +enum Tfa98xx_Error tfa98xx_dsp_support_drc(Tfa98xx_handle_t handle, + int *pbSupportDrc); + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(Tfa98xx_handle_t handle, int *pbSupportFramework); + +/** + * read the speaker excursion model as used by SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_excursion_model( + Tfa98xx_handle_t handle, + int length, + unsigned char *pSpeakerBytes); + +/** + * load all the parameters for a preset from a file + */ +enum Tfa98xx_Error tfa98xx_dsp_write_preset(Tfa98xx_handle_t handle, + int length, const unsigned char + *pPresetBytes); + +/** + * wrapper for dsp_msg that adds opcode and only writes + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + const unsigned char data[]); + +/** + * wrapper for dsp_msg that writes opcode and reads back the data + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write_read(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]); + +/** + * wrapper for dsp_msg that adds opcode and 3 bytes required for coefs + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_coefs(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]); + +/** + * wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics(Tfa98xx_handle_t handle, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]); + +/** + * Disable a certain biquad. + * @param handle to opened instance + * @param biquad_index: 1-10 of the biquad that needs to be adressed +*/ +enum Tfa98xx_Error Tfa98xx_DspBiquad_Disable(Tfa98xx_handle_t handle, + int biquad_index); + +/** + * fill the calibration value as milli ohms in the struct + * assume that the device has been calibrated + */ +enum Tfa98xx_Error +tfa_dsp_get_calibration_impedance(Tfa98xx_handle_t handle); + +/* + * return the mohm value + */ +int tfa_get_calibration_info(Tfa98xx_handle_t handle, int channel); + +/** + * Reads a number of words from dsp memory + * @param handle to opened instance + * @param subaddress write address to set in address register + * @param pValue pointer to read data +*/ +enum Tfa98xx_Error tfa98xx_read_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short *pValue); + +/** + * Reads a number of words from dsp memory + * @param handle to opened instance + * @param subaddress write address to set in address register + * @param value value to write int the memory +*/ +enum Tfa98xx_Error tfa98xx_write_register16(Tfa98xx_handle_t handle, + unsigned char subaddress, + unsigned short value); + +/** + * Reads a number of words from dsp memory + * @param handle to opened instance + * @param start_offset offset from where to start reading + * @param num_words number of words to read + * @param pValues pointer to read data +*/ +enum Tfa98xx_Error tfa98xx_dsp_read_mem(Tfa98xx_handle_t handle, + unsigned int start_offset, + int num_words, int *pValues); + +/** + * Write a value to dsp memory + * @param handle to opened instance + * @param address write address to set in address register + * @param value value to write int the memory + * @param memtype type of memory to write to +*/ +enum Tfa98xx_Error tfa98xx_dsp_write_mem_word(Tfa98xx_handle_t handle, + unsigned short address, + int value, int memtype); + +/** + * Read data from dsp memory + * @param handle to opened instance + * @param subaddress write address to set in address register + * @param num_bytes number of bytes to read from dsp + * @param data the unsigned char buffer to read data into +*/ +enum Tfa98xx_Error tfa98xx_read_data(Tfa98xx_handle_t handle, + unsigned char subaddress, + int num_bytes, unsigned char data[]); + +/** + * Write all the bytes specified by num_bytes and data to dsp memory + * @param handle to opened instance + * @param subaddress the subaddress to write to + * @param num_bytes number of bytes to write + * @param data actual data to write +*/ +enum Tfa98xx_Error tfa98xx_write_data(Tfa98xx_handle_t handle, + unsigned char subaddress, + int num_bytes, + const unsigned char data[]); + +enum Tfa98xx_Error tfa98xx_write_raw(Tfa98xx_handle_t handle, + int num_bytes, + const unsigned char data[]); + +/* support for converting error codes into text */ +const char *tfa98xx_get_error_string(enum Tfa98xx_Error error); + +/** + * convert signed 24 bit integers to 32bit aligned bytes + * input: data contains "num_bytes/3" int24 elements + * output: bytes contains "num_bytes" byte elements + * @param num_data length of the input data array + * @param data input data as integer array + * @param bytes output data as unsigned char array +*/ +void tfa98xx_convert_data2bytes(int num_data, const int data[], + unsigned char bytes[]); + +/* return the device revision id + */ +unsigned short tfa98xx_get_device_revision(Tfa98xx_handle_t handle); + +/** + * return the device digital audio interface (DAI) type bitmap + */ +enum Tfa98xx_DAI tfa98xx_get_device_dai(Tfa98xx_handle_t handle); + +/** + * convert memory bytes to signed 24 bit integers + * input: bytes contains "num_bytes" byte elements + * output: data contains "num_bytes/3" int24 elements + * @param num_bytes length of the input data array + * @param bytes input data as unsigned char array + * @param data output data as integer array +*/ +void tfa98xx_convert_bytes2data(int num_bytes, const unsigned char bytes[], + int data[]); + +/** + * Read a part of the dsp memory + * @param handle to opened instance + * @param memoryType indicator to the memory type + * @param offset from where to start reading + * @param length the number of bytes to read + * @param bytes output data as unsigned char array +*/ +enum Tfa98xx_Error tfa98xx_dsp_get_memory(Tfa98xx_handle_t handle, int memoryType, + int offset, int length, unsigned char bytes[]); + +/** + * Write a value to the dsp memory + * @param handle to opened instance + * @param memoryType indicator to the memory type + * @param offset from where to start writing + * @param length the number of bytes to write + * @param value the value to write to the dsp +*/ +enum Tfa98xx_Error tfa98xx_dsp_set_memory(Tfa98xx_handle_t handle, int memoryType, + int offset, int length, int value); + +enum Tfa98xx_Error tfa98xx_dsp_write_config(Tfa98xx_handle_t handle, int length, const unsigned char *p_config_bytes); +enum Tfa98xx_Error tfa98xx_dsp_write_drc(Tfa98xx_handle_t handle, int length, const unsigned char *p_drc_bytes); + +//TODO define +/** + * write/read raw msg functions : + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * @param handle to opened instance + * @param length length of the character buffer to write + * @param buf character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg(Tfa98xx_handle_t handle, int length, const char *buf); + +/** + * write/read raw msg functions: + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * An ID is added to modify the command-ID + * @param handle to opened instance + * @param length length of the character buffer to write + * @param buf character buffer to write + * @param cmdid command identifier +*/ +enum Tfa98xx_Error tfa_dsp_msg_id(Tfa98xx_handle_t handle, int length, const char *buf, uint8_t cmdid[3]); + +/** + * write raw dsp msg functions + * @param handle to opened instance + * @param length length of the character buffer to write + * @param buffer character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg_write(Tfa98xx_handle_t handle, int length, const char *buffer); + +/** + * write raw dsp msg functions + * @param handle to opened instance + * @param length length of the character buffer to write + * @param buf character buffer to write + * @param cmdid command identifier +*/ +enum Tfa98xx_Error tfa_dsp_msg_write_id(Tfa98xx_handle_t handle, int length, const char *buffer, uint8_t cmdid[3]); + +/** + * status function used by tfa_dsp_msg() to retrieve command/msg status: + * return a <0 status of the DSP did not ACK. + * @param handle to opened instance + * @param pRpcStatus status for remote processor communication +*/ +enum Tfa98xx_Error tfa_dsp_msg_status(Tfa98xx_handle_t handle, int *pRpcStatus); + +/** + * Read a message from dsp + * @param length number of bytes of the message + * @param bytes pointer to unsigned char buffer +*/ +enum Tfa98xx_Error tfa_dsp_msg_read(Tfa98xx_handle_t handle,int length, unsigned char *bytes); + +void create_dsp_buffer_msg(nxpTfaMsg_t *msg, char *buffer, int *size); + +int tfa_set_bf(Tfa98xx_handle_t dev_idx, const uint16_t bf, const uint16_t value); +int tfa_set_bf_volatile(Tfa98xx_handle_t dev_idx, const uint16_t bf, const uint16_t value); + +/** + * Get the value of a given bitfield + * @param dev_idx this is the device index + * @param bf the value indicating which bitfield + */ +int tfa_get_bf(Tfa98xx_handle_t dev_idx, const uint16_t bf); + +/** + * Set the value of a given bitfield + * @param bf the value indicating which bitfield + * @param bf_value the value of the bitfield + * @param p_reg_value a pointer to the register where to write the bitfield value + */ +int tfa_set_bf_value(const uint16_t bf, const uint16_t bf_value, uint16_t *p_reg_value); + +uint16_t tfa_get_bf_value(const uint16_t bf, const uint16_t reg_value); +int tfa_write_reg(Tfa98xx_handle_t dev_idx, const uint16_t bf, const uint16_t reg_value); +int tfa_read_reg(Tfa98xx_handle_t dev_idx, const uint16_t bf); + +/* bitfield */ +/** + * get the datasheet name corresponding to the bitfield number + * @param num is the number for which to get the bitfield name + * @param rev is the device type + */ +char *tfaContBfName(uint16_t num, unsigned short rev); + +/** + * get the bitfield name corresponding to the bitfield number + * @param num is the number for which to get the bitfield name + * @param rev is the device type + */ +char *tfaContBitName(uint16_t num, unsigned short rev); + +/** + * get the bitfield number corresponding to the bitfield name + * @param name is the bitfield name for which to get the bitfield number + * @param rev is the device type + */ +uint16_t tfaContBfEnum(const char *name, unsigned short rev); + +/** +* get the bitfield number corresponding to the bitfield name, checks for all devices +* @param name is the bitfield name for which to get the bitfield number + */ +uint16_t tfaContBfEnumAny(const char *name); + +#define TFA_FAM(dev_idx, fieldname) ((tfa98xx_dev_family(dev_idx) == 1) ? TFA1_BF_##fieldname : TFA2_BF_##fieldname) +#define TFA_FAM_FW(dev_idx, fwname) ((tfa98xx_dev_family(dev_idx) == 1) ? TFA1_FW_##fwname : TFA2_FW_##fwname) + +/* set/get bit fields to HW register*/ +#define TFA_SET_BF(dev_idx, fieldname, value) tfa_set_bf(dev_idx, TFA_FAM(dev_idx, fieldname), value) +#define TFA_SET_BF_VOLATILE(dev_idx, fieldname, value) tfa_set_bf_volatile(dev_idx, TFA_FAM(dev_idx, fieldname), value) +#define TFA_GET_BF(dev_idx, fieldname) tfa_get_bf(dev_idx, TFA_FAM(dev_idx, fieldname)) + +/* set/get bit field in variable */ +#define TFA_SET_BF_VALUE(dev_idx, fieldname, bf_value, p_reg_value) tfa_set_bf_value(TFA_FAM(dev_idx, fieldname), bf_value, p_reg_value) +#define TFA_GET_BF_VALUE(dev_idx, fieldname, reg_value) tfa_get_bf_value(TFA_FAM(dev_idx, fieldname), reg_value) + +/* write/read registers using a bit field name to determine the register address */ +#define TFA_WRITE_REG(dev_idx, fieldname, value) tfa_write_reg(dev_idx, TFA_FAM(dev_idx, fieldname), value) +#define TFA_READ_REG(dev_idx, fieldname) tfa_read_reg(dev_idx, TFA_FAM(dev_idx, fieldname)) + +/* FOR CALIBRATION RETRIES */ +#define TFA98XX_API_WAITRESULT_NTRIES 3000 // defined in API + +/** + * run the startup/init sequence and set ACS bit + * @param state the cold start state that is requested + */ +enum Tfa98xx_Error tfaRunColdboot(Tfa98xx_handle_t handle, int state); +enum Tfa98xx_Error tfaRunMute(Tfa98xx_handle_t handle); +enum Tfa98xx_Error tfaRunUnmute(Tfa98xx_handle_t handle); + +/** + * wait for calibrateDone + * @param calibrateDone pointer to status of calibration + */ +enum Tfa98xx_Error tfaRunWaitCalibration(Tfa98xx_handle_t handle, int *calibrateDone); + +/** + * run the startup/init sequence and set ACS bit + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunColdStartup(Tfa98xx_handle_t handle, int profile); + +/** + * this will load the patch witch will implicitly start the DSP + * if no patch is available the DPS is started immediately + */ +enum Tfa98xx_Error tfaRunStartDSP(Tfa98xx_handle_t handle); + +/** + * start the clocks and wait until the AMP is switching + * on return the DSP sub system will be ready for loading + * @param profile the profile that should be loaded on startup + */ +enum Tfa98xx_Error tfaRunStartup(Tfa98xx_handle_t handle, int profile); + +/** + * start the maximus speakerboost algorithm + * this implies a full system startup when the system was not already started + * @param force indicates wether a full system startup should be allowed + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunSpeakerBoost(Tfa98xx_handle_t handle, int force, int profile); + +/** + * Startup the device and write all files from device and profile section + * @param force indicates wether a full system startup should be allowed + * @param profile the profile that should be loaded on speaker startup + */ +enum Tfa98xx_Error tfaRunSpeakerStartup(Tfa98xx_handle_t handle, int force, int profile); + +/** + * Run calibration + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(Tfa98xx_handle_t handle, int profile); + +/** + * startup all devices. all step until patch loading is handled + */ +int tfaRunStartupAll(Tfa98xx_handle_t *handles); + +/** + * powerup the coolflux subsystem and wait for it + */ +enum Tfa98xx_Error tfa_cf_powerup(Tfa98xx_handle_t handle); + +/* + * print the current device manager state + */ +enum Tfa98xx_Error show_current_state(Tfa98xx_handle_t handle); + +/** + * set verbosity level + */ +void tfa_verbose(int level); + +/** + * Init registers and coldboot dsp + */ +int tfa98xx_reset(Tfa98xx_handle_t handle); + +/** + * + * @param dev_idx is the device index + * @param revid is the revision id + * @param slave_address is the slave address + */ +void tfa_mock_probe(int dev_idx, unsigned short revid, int slave_address); + +/** + * + * @param dev_idx is the device index + * @param revid is the revision id + */ +enum Tfa98xx_Error tfa_soft_probe(int dev_idx, int revid); + +/** + * Get profile from a register + */ +int tfa_get_swprof(Tfa98xx_handle_t handle); + +/** + * Save profile in a register + */ +int tfa_set_swprof(Tfa98xx_handle_t handle, unsigned short new_value); + +int tfa_get_swvstep(Tfa98xx_handle_t handle); + +int tfa_set_swvstep(Tfa98xx_handle_t handle, unsigned short new_value); + +#ifdef __cplusplus +} +#endif +#endif /* TFA_SERVICE_H */ diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 997a623033fb..d71241332dc6 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -30,7 +30,6 @@ #include #include "wcd-mbhc-v2.h" #include "wcdcal-hwdep.h" - #define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \ @@ -40,7 +39,7 @@ SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ SND_JACK_BTN_4 | SND_JACK_BTN_5 ) #define OCP_ATTEMPT 20 -#define HS_DETECT_PLUG_TIME_MS (3 * 1000) +#define HS_DETECT_PLUG_TIME_MS (800) #define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 @@ -651,6 +650,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->zl = mbhc->zr = 0; pr_debug("%s: Reporting removal %d(%x)\n", __func__, jack_type, mbhc->hph_status); +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + switch_set_state(&mbhc->wcd9xxx_sdev, 0); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status, WCD_MBHC_JACK_MASK); wcd_mbhc_set_and_turnoff_hph_padac(mbhc); @@ -763,8 +764,29 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_status |= jack_type; - pr_debug("%s: Reporting insertion %d(%x)\n", __func__, - jack_type, mbhc->hph_status); +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + switch (mbhc->current_plug) { + case MBHC_PLUG_TYPE_HEADPHONE: + case MBHC_PLUG_TYPE_HIGH_HPH: + mbhc->mbhc_cfg->headset_type = 0; + switch_set_state(&mbhc->wcd9xxx_sdev, 2); + break; + case MBHC_PLUG_TYPE_GND_MIC_SWAP: + mbhc->mbhc_cfg->headset_type = 0; + switch_set_state(&mbhc->wcd9xxx_sdev, 1); + break; + case MBHC_PLUG_TYPE_HEADSET: + mbhc->mbhc_cfg->headset_type = 1; + switch_set_state(&mbhc->wcd9xxx_sdev, 1); + break; + default: + mbhc->mbhc_cfg->headset_type = 0; + switch_set_state(&mbhc->wcd9xxx_sdev, 0); + break; + } + pr_err("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, (mbhc->hph_status | SND_JACK_MECHANICAL), WCD_MBHC_JACK_MASK); @@ -1248,6 +1270,9 @@ static void wcd_correct_swch_plug(struct work_struct *work) else plug_type = MBHC_PLUG_TYPE_INVALID; } +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: Valid plug found, plug type is %d\n", + __func__, plug_type); do { cross_conn = wcd_check_cross_conn(mbhc); @@ -1359,7 +1384,8 @@ correct_plug_type: * This is due to GND/MIC switch didn't * work, Report unsupported plug. */ - pr_debug("%s: switch didnt work\n", +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: switch didn't work\n", __func__); plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; goto report; @@ -1386,7 +1412,8 @@ correct_plug_type: */ if (mbhc->mbhc_cfg->swap_gnd_mic && mbhc->mbhc_cfg->swap_gnd_mic(codec)) { - pr_debug("%s: US_EU gpio present,flip switch\n" +/*zhiguang.su@MultiMedia.AudioDrv , 2016/7/6, add for debug*/ + pr_err("%s: US_EU gpio present,flip switch\n" , __func__); continue; } @@ -1396,12 +1423,14 @@ correct_plug_type: WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); if (hs_comp_res && !(hphl_sch || mic_sch)) { - pr_debug("%s: cable is extension cable\n", __func__); +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: cable is extension cable\n", __func__); plug_type = MBHC_PLUG_TYPE_HIGH_HPH; wrk_complete = true; } else { - pr_debug("%s: cable might be headset: %d\n", __func__, - plug_type); +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: cable might be headset: %d mbhc->current_plug = %d\n", + __func__, plug_type, mbhc->current_plug); if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { plug_type = MBHC_PLUG_TYPE_HEADSET; if (!spl_hs_reported && @@ -1425,7 +1454,8 @@ correct_plug_type: MBHC_PLUG_TYPE_ANC_HEADPHONE)) && !wcd_swch_level_remove(mbhc) && !mbhc->btn_press_intr) { - pr_debug("%s: cable is %sheadset\n", +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: cable is %sheadset\n", __func__, ((spl_hs_count == WCD_MBHC_SPL_HS_CNT) ? @@ -1437,7 +1467,8 @@ correct_plug_type: } } if (!wrk_complete && mbhc->btn_press_intr) { - pr_debug("%s: Can be slow insertion of headphone\n", __func__); +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: Can be slow insertion of headphone\n", __func__); wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } @@ -1447,7 +1478,8 @@ correct_plug_type: */ if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { - pr_debug("%s: plug_type:0x%x already reported\n", +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: plug_type:0x%x already reported\n", __func__, mbhc->current_plug); goto enable_supply; } @@ -1464,7 +1496,8 @@ correct_plug_type: report: if (wcd_swch_level_remove(mbhc)) { - pr_debug("%s: Switch level is low\n", __func__); +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: Switch level is low\n", __func__); goto exit; } if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { @@ -1472,7 +1505,8 @@ report: wcd_cancel_btn_work(mbhc); plug_type = MBHC_PLUG_TYPE_HEADPHONE; } - pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", +/*zhiguang.su@MultiMedia.AudioDrv , 2016/3/18, modify for debug*/ + pr_err("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", __func__, plug_type, wrk_complete, mbhc->btn_press_intr); WCD_MBHC_RSC_LOCK(mbhc); @@ -1877,6 +1911,8 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + pr_debug("%s: hphl_sch %#x, mic_sch %#x, hs_comp_result %#x\n", + __func__, hphl_sch, mic_sch, hs_comp_result); if (removed) { if (!(hphl_sch && mic_sch && hs_comp_result)) { /* @@ -2033,6 +2069,11 @@ static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) goto done; } mbhc->buttons_pressed |= mask; + +/*zhiguang.su@MultiMedia.AudioDrv , 2016/5/24, add for debug*/ + pr_err("%s mbhc->buttons_pressed = %x\n", + __func__, mbhc->buttons_pressed); + mbhc->mbhc_cb->lock_sleep(mbhc, true); if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0) { @@ -2050,7 +2091,8 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) struct wcd_mbhc *mbhc = data; int ret; - pr_debug("%s: enter\n", __func__); +/*zhiguang.su@MultiMedia.AudioDrv , 2016/5/24, add for debug*/ + pr_err("%s\n", __func__); WCD_MBHC_RSC_LOCK(mbhc); if (wcd_swch_level_remove(mbhc)) { pr_debug("%s: Switch level is low ", __func__); @@ -2070,11 +2112,20 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) * headset not headphone. */ if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + + /*zhiguang.su@MultiMedia.AudioDrv , 2016/5/24, add for debug*/ + pr_err("%s MBHC_PLUG_TYPE_HEADPHONE\n", __func__); + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); goto exit; } if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { + + /*zhiguang.su@MultiMedia.AudioDrv , 2016/5/24, add for debug*/ + pr_err("%s mbhc->buttons_pressed = %x\n", __func__, + mbhc->buttons_pressed); + ret = wcd_cancel_btn_work(mbhc); if (ret == 0) { pr_debug("%s: Reporting long button release event\n", diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 09b9307ad61d..9cca2e14dd11 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -12,6 +12,11 @@ #ifndef __WCD_MBHC_V2_H__ #define __WCD_MBHC_V2_H__ +/*zhiguang.su@MultiMedia.AudioDrv, + *2015-10-26, + *Modify for headset uevent report + */ +#include #include #include #include @@ -274,6 +279,11 @@ struct wcd_mbhc_config { bool gnd_det_en; int key_code[WCD_MBHC_KEYCODE_NUM]; uint32_t linein_th; +/*zhiguang.su@MultiMedia.AudioDrv, + *2015-10-26, + *Modify for headset uevent + */ + int headset_type; bool moisture_en; int mbhc_micbias; int anc_micbias; @@ -461,6 +471,8 @@ struct wcd_mbhc { struct completion btn_press_compl; struct mutex hphl_pa_lock; struct mutex hphr_pa_lock; +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + struct switch_dev wcd9xxx_sdev; unsigned long intr_status; bool is_hph_ocp_pending; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 2bc911e63e12..a576d47d9df5 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -191,8 +191,8 @@ module_param(sido_buck_svs_voltage, int, S_IRUGO | S_IWUSR | S_IWGRP); MODULE_PARM_DESC(sido_buck_svs_voltage, "setting for SVS voltage for SIDO BUCK"); - -#define TASHA_TX_UNMUTE_DELAY_MS 40 +#define TASHA_TX_UNMUTE_DELAY_MS 50 +#define HEADSET_TYPE_NAME_LEN 64 static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS; module_param(tx_unmute_delay, int, @@ -881,7 +881,35 @@ static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, }; +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ +enum { + NO_DEVICE = 0, + HS_WITH_MIC = 1, + HS_WITHOUT_MIC = 2, +}; +struct tasha_priv *priv_headset_type; + +static ssize_t wcd9xxx_print_name(struct switch_dev *sdev, char *buf) +{ + + switch (switch_get_state(sdev)) { + case NO_DEVICE: + return snprintf(buf, HEADSET_TYPE_NAME_LEN, "No Device\n"); + case HS_WITH_MIC: + if (priv_headset_type->mbhc.mbhc_cfg->headset_type == 1) + return snprintf(buf, HEADSET_TYPE_NAME_LEN, + "American Headset\n"); + else + return snprintf(buf, HEADSET_TYPE_NAME_LEN, + "Headset\n"); + case HS_WITHOUT_MIC: + return snprintf(buf, HEADSET_TYPE_NAME_LEN, + "Handset\n"); + + } + return -EINVAL; +} /* * wcd9335_get_codec_info: Get codec specific information * @@ -2291,6 +2319,45 @@ static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol, return 0; } +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for l21 power*/ +static int tasha_get_Bob_Power(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + return 0; +} + +static int tasha_put_Bob_Power(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int mode; + pr_err("%s value = %d\n", + __func__, (uint32_t)ucontrol->value.integer.value[0]); + + if (bob_power == NULL) + return 0; + + if (ucontrol->value.integer.value[0] == 1) { + pr_err("%s enable vreg_bob power\n", __func__); + ret = regulator_set_mode(bob_power, REGULATOR_MODE_FAST); + if (ret) + pr_err("%s fail to enable vreg_bob power\n", __func__); + } else { + pr_err("%s disable vreg_bob power\n", __func__); + mode = regulator_get_mode(bob_power); + if (mode == REGULATOR_MODE_FAST) { + ret = regulator_set_mode(bob_power, + REGULATOR_MODE_NORMAL); + if (ret) + pr_err("%s fail to disable vreg_bob power\n", + __func__); + } + } + + return 0; +} + static int tasha_get_anc_func(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -8587,6 +8654,10 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = { SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot, tasha_put_anc_slot), + +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for l21 power*/ + SOC_SINGLE_EXT("BOB Power", SND_SOC_NOPM, 0, 100, 0, + tasha_get_Bob_Power, tasha_put_Bob_Power), SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func, tasha_put_anc_func), @@ -13586,6 +13657,9 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) void *ptr = NULL; struct regulator *supply; +/*zhiguang.su@MultiMedia.AudioDrv, 2017-03-27, add for debug*/ +pr_err("%s enter\n", __func__); + control = dev_get_drvdata(codec->dev->parent); dev_info(codec->dev, "%s()\n", __func__); @@ -13674,6 +13748,13 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) goto err_hwdep; } +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + tasha->mbhc.wcd9xxx_sdev.name = "h2w"; + tasha->mbhc.wcd9xxx_sdev.print_name = wcd9xxx_print_name; + ret = switch_dev_register(&tasha->mbhc.wcd9xxx_sdev); + if (ret) + goto err_switch_dev_register; + ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) + sizeof(tasha_tx_chs)), GFP_KERNEL); if (!ptr) { @@ -13785,12 +13866,19 @@ static int tasha_codec_probe(struct snd_soc_codec *codec) mutex_unlock(&tasha->codec_mutex); snd_soc_dapm_sync(dapm); + +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + priv_headset_type = tasha; + return ret; err_pdata: devm_kfree(codec->dev, ptr); control->rx_chs = NULL; control->tx_chs = NULL; +/*zhiguang.su@MultiMedia.AudioDrv, 2015-10-26, Modify for headset uevent*/ + switch_dev_unregister(&tasha->mbhc.wcd9xxx_sdev); +err_switch_dev_register: err_hwdep: devm_kfree(codec->dev, tasha->fw_data); tasha->fw_data = NULL; @@ -14285,6 +14373,8 @@ static int tasha_probe(struct platform_device *pdev) struct wcd9xxx_resmgr_v2 *resmgr; struct wcd9xxx_power_region *cdc_pwr; +/*zhiguang.su@MultiMedia.AudioDrv, 2017-03-27, add for debug*/ +pr_err("%s enter\n", __func__); if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { dev_err(&pdev->dev, "%s: dsp down\n", __func__); @@ -14369,6 +14459,9 @@ static int tasha_probe(struct platform_device *pdev) else tasha->wcd_native_clk = wcd_native_clk; +/*zhiguang.su@MultiMedia.AudioDrv, 2017-03-27, add for debug*/ +pr_err("%s snd_soc_register_codec\n", __func__); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, tasha_dai, ARRAY_SIZE(tasha_dai)); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h index 8d38399ba92f..f124a3338d5a 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/sound/soc/codecs/wcd9335.h @@ -175,6 +175,9 @@ enum { RX_GAIN_OFFSET_0_DB, }; +/*zhiguang.su@MultiMedia.AudioDrv, 2014-4-14, add for l21 power*/ +extern struct regulator *bob_power; + extern void *tasha_get_afe_config(struct snd_soc_codec *codec, enum afe_config_type config_type); extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index 1e97a0cd76ac..4448466b4508 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -522,6 +522,9 @@ static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable, bool dapm); static int msm_wsa881x_init(struct snd_soc_component *component); +/*zhiguang.su@MultiMedia.AudioDrv , 2017/6/6, read project string*/ +int op_project_17801; + /* * Need to report LINEIN * if R/L channel impedance is larger than 5K ohm @@ -534,9 +537,9 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { .swap_gnd_mic = NULL, .hs_ext_micbias = true, .key_code[0] = KEY_MEDIA, - .key_code[1] = KEY_VOICECOMMAND, - .key_code[2] = KEY_VOLUMEUP, - .key_code[3] = KEY_VOLUMEDOWN, + .key_code[1] = KEY_VOLUMEUP, + .key_code[2] = KEY_VOLUMEDOWN, + .key_code[3] = 0, .key_code[4] = 0, .key_code[5] = 0, .key_code[6] = 0, @@ -3035,6 +3038,39 @@ static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { SND_SOC_DAPM_MIC("Digital Mic5", NULL), }; +/*zhiguang.su@MultiMedia.AudioDrv , 2017/06/06, add for 17801 project*/ +static const struct snd_soc_dapm_widget msm_dapm_widgets_17801[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + msm_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, + msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_3 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_4 amp", NULL), + SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event), + SND_SOC_DAPM_MIC("Handset Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL), + SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL), +/*zhiguang.su@MultiMedia.AudioDrv , 2017/05/27, add for analog mic*/ + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + SND_SOC_DAPM_MIC("Analog Mic6", NULL), + + SND_SOC_DAPM_MIC("Digital Mic0", NULL), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + SND_SOC_DAPM_MIC("Digital Mic3", NULL), + SND_SOC_DAPM_MIC("Digital Mic4", NULL), + SND_SOC_DAPM_MIC("Digital Mic5", NULL), +}; + static inline int param_is_mask(int p) { return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && @@ -3671,8 +3707,14 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) return ret; } - snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, - ARRAY_SIZE(msm_dapm_widgets)); +/*zhiguang.su@MultiMedia.AudioDrv , 2017/05/27, add for analog mic*/ + if (op_project_17801) { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets_17801, + ARRAY_SIZE(msm_dapm_widgets_17801)); + } else { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + } if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha, @@ -3691,6 +3733,11 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3"); snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4"); snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5"); +/*zhiguang.su@MultiMedia.AudioDrv , 2017/05/27, add for analog mic*/ + if (op_project_17801) { + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic3"); + snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4"); + } snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5"); snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6"); snd_soc_dapm_ignore_suspend(dapm, "MADINPUT"); @@ -3803,13 +3850,6 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) pdata->codec_root = entry; tavil_codec_info_create_codec_entry(pdata->codec_root, codec); } else { - if (rtd->card->num_aux_devs && rtd_aux && rtd_aux->component) - if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || - !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { - tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1); - tasha_set_spkr_gain_offset(rtd->codec, - RX_GAIN_OFFSET_M1P5_DB); - } card = rtd->card->snd_card; entry = snd_register_module_info(card->module, "codecs", card->proc_root); @@ -3853,7 +3893,8 @@ static void *def_tasha_mbhc_cal(void) return NULL; #define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y)) - S(v_hs_max, 1600); + S(v_hs_max, 1700); + #undef S #define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y)) S(num_btn, WCD_MBHC_DEF_BUTTONS); @@ -3862,11 +3903,10 @@ static void *def_tasha_mbhc_cal(void) btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal); btn_high = ((void *)&btn_cfg->_v_btn_low) + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); - btn_high[0] = 75; - btn_high[1] = 150; - btn_high[2] = 237; - btn_high[3] = 500; + btn_high[1] = 213; + btn_high[2] = 450; + btn_high[3] = 470; btn_high[4] = 500; btn_high[5] = 500; btn_high[6] = 500; @@ -4324,8 +4364,8 @@ static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, } if (pinctrl_info->pinctrl == NULL) { - pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); - ret = -EINVAL; + pr_debug("%s: pinctrl_info->pinctrl is NULL\n", __func__); + /*ret = -EINVAL;*/ goto err; } @@ -6502,8 +6542,8 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .stream_name = "Quaternary MI2S Playback", .cpu_dai_name = "msm-dai-q6-mi2s.3", .platform_name = "msm-pcm-routing", - .codec_name = "msm-stub-codec.1", - .codec_dai_name = "msm-stub-rx", + .codec_name = "tfa98xx.9-0036", + .codec_dai_name = "tfa98xx_codec-9-36", .no_pcm = 1, .dpcm_playback = 1, .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, @@ -6881,9 +6921,14 @@ static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd) return ret; } - snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, - ARRAY_SIZE(msm_dapm_widgets)); - +/*zhiguang.su@MultiMedia.AudioDrv , 2017/05/27, add for analog mic*/ + if (op_project_17801) { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets_17801, + ARRAY_SIZE(msm_dapm_widgets_17801)); + } else { + snd_soc_dapm_new_controls(dapm, msm_dapm_widgets, + ARRAY_SIZE(msm_dapm_widgets)); + } return 0; } @@ -7215,7 +7260,8 @@ static int msm_init_wsa_dev(struct platform_device *pdev, char *dev_name_str = NULL; int found = 0; int ret = 0; - +/*wangdongdong@MultiMediaService,add to avoid wsa init*/ + return ret; /* Get maximum WSA device count for this platform */ ret = of_property_read_u32(pdev->dev.of_node, "qcom,wsa-max-devs", &wsa_max_devs); @@ -7481,6 +7527,14 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, pdata); +/*zhiguang.su@MultiMedia.AudioDrv , 2017/6/6, read project string*/ + ret = of_property_read_bool(card->dev->of_node, "op,project_17801"); + if (ret) + op_project_17801 = 0; + else + op_project_17801 = 1; + pr_err("%s project name: %d", __func__, op_project_17801); + ret = snd_soc_of_parse_card_name(card, "qcom,model"); if (ret) { dev_err(&pdev->dev, "parse card name failed, err:%d\n", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index d0dabcb4e337..9248667a2c49 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -15378,7 +15378,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX_DL_HL", "Switch", "TERT_MI2S_DL_HL"}, {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_DL_HL"}, - {"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"}, + /* modify engineer echo_test route + *{"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"}, + */ + {"QUAT_MI2S_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, + {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"}, {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, {"INT3_MI2S_UL_HL", NULL, "INT3_MI2S_TX"},